diff options
Diffstat (limited to 'src/mongo/util/options_parser')
19 files changed, 7517 insertions, 7613 deletions
diff --git a/src/mongo/util/options_parser/constraints.cpp b/src/mongo/util/options_parser/constraints.cpp index 8c79138a44d..1ed195e19c6 100644 --- a/src/mongo/util/options_parser/constraints.cpp +++ b/src/mongo/util/options_parser/constraints.cpp @@ -35,107 +35,104 @@ namespace mongo { namespace optionenvironment { - Status NumericKeyConstraint::check(const Environment& env) { - Value val; - Status s = env.get(_key, &val); +Status NumericKeyConstraint::check(const Environment& env) { + Value val; + Status s = env.get(_key, &val); - if (s == ErrorCodes::NoSuchKey) { - // Key not set, skipping numeric constraint check - return Status::OK(); - } - - // The code that controls whether a type is "compatible" is contained in the Value - // class, so if that handles compatibility between numeric types then this will too. - long intVal; - if (val.get(&intVal).isOK()) { - if (intVal < _min || intVal > _max) { - StringBuilder sb; - sb << "Error: Attempting to set " << _key << " to value: " << - intVal << " which is out of range: (" << - _min << "," << _max << ")"; - return Status(ErrorCodes::BadValue, sb.str()); - } - } - else { - StringBuilder sb; - sb << "Error: " << _key << " is of type: " << val.typeToString() << - " but must be of a numeric type."; - return Status(ErrorCodes::BadValue, sb.str()); - } + if (s == ErrorCodes::NoSuchKey) { + // Key not set, skipping numeric constraint check return Status::OK(); } - Status ImmutableKeyConstraint::check(const Environment& env) { - Value env_value; - Status ret = env.get(_key, &env_value); - if (ret.isOK()) { - if (_value.isEmpty()) { - _value = env_value; - } - else { - if (!_value.equal(env_value)) { - StringBuilder sb; - sb << "Error: " << _key << " is immutable once set"; - return Status(ErrorCodes::BadValue, sb.str()); - } - } + // The code that controls whether a type is "compatible" is contained in the Value + // class, so if that handles compatibility between numeric types then this will too. + long intVal; + if (val.get(&intVal).isOK()) { + if (intVal < _min || intVal > _max) { + StringBuilder sb; + sb << "Error: Attempting to set " << _key << " to value: " << intVal + << " which is out of range: (" << _min << "," << _max << ")"; + return Status(ErrorCodes::BadValue, sb.str()); } - - return ret; + } else { + StringBuilder sb; + sb << "Error: " << _key << " is of type: " << val.typeToString() + << " but must be of a numeric type."; + return Status(ErrorCodes::BadValue, sb.str()); } - - Status MutuallyExclusiveKeyConstraint::check(const Environment& env) { - Value env_value; - Status ret = env.get(_key, &env_value); - if (ret.isOK()) { - ret = env.get(_otherKey, &env_value); - if (ret.isOK()) { + return Status::OK(); +} + +Status ImmutableKeyConstraint::check(const Environment& env) { + Value env_value; + Status ret = env.get(_key, &env_value); + if (ret.isOK()) { + if (_value.isEmpty()) { + _value = env_value; + } else { + if (!_value.equal(env_value)) { StringBuilder sb; - sb << _otherKey << " is not allowed when " << _key << " is specified"; + sb << "Error: " << _key << " is immutable once set"; return Status(ErrorCodes::BadValue, sb.str()); } } - - return Status::OK(); } - Status RequiresOtherKeyConstraint::check(const Environment& env) { - Value env_value; - Status ret = env.get(_key, &env_value); + return ret; +} + +Status MutuallyExclusiveKeyConstraint::check(const Environment& env) { + Value env_value; + Status ret = env.get(_key, &env_value); + if (ret.isOK()) { + ret = env.get(_otherKey, &env_value); if (ret.isOK()) { - ret = env.get(_otherKey, &env_value); - if (!ret.isOK()) { - StringBuilder sb; - sb << _otherKey << " is required when " << _key << " is specified"; - return Status(ErrorCodes::BadValue, sb.str()); - } + StringBuilder sb; + sb << _otherKey << " is not allowed when " << _key << " is specified"; + return Status(ErrorCodes::BadValue, sb.str()); } + } - return Status::OK(); + return Status::OK(); +} + +Status RequiresOtherKeyConstraint::check(const Environment& env) { + Value env_value; + Status ret = env.get(_key, &env_value); + if (ret.isOK()) { + ret = env.get(_otherKey, &env_value); + if (!ret.isOK()) { + StringBuilder sb; + sb << _otherKey << " is required when " << _key << " is specified"; + return Status(ErrorCodes::BadValue, sb.str()); + } } - Status StringFormatKeyConstraint::check(const Environment& env) { - Value value; - Status ret = env.get(_key, &value); - if (ret.isOK()) { - std::string stringVal; - ret = value.get(&stringVal); - if (!ret.isOK()) { - StringBuilder sb; - sb << _key << " could not be read as a string: " << ret.reason(); - return Status(ErrorCodes::BadValue, sb.str()); - } + return Status::OK(); +} - pcrecpp::RE re(_regexFormat); - if (!re.FullMatch(stringVal)) { - StringBuilder sb; - sb << _key << " must be a string of the format: " << _displayFormat; - return Status(ErrorCodes::BadValue, sb.str()); - } +Status StringFormatKeyConstraint::check(const Environment& env) { + Value value; + Status ret = env.get(_key, &value); + if (ret.isOK()) { + std::string stringVal; + ret = value.get(&stringVal); + if (!ret.isOK()) { + StringBuilder sb; + sb << _key << " could not be read as a string: " << ret.reason(); + return Status(ErrorCodes::BadValue, sb.str()); } - return Status::OK(); + pcrecpp::RE re(_regexFormat); + if (!re.FullMatch(stringVal)) { + StringBuilder sb; + sb << _key << " must be a string of the format: " << _displayFormat; + return Status(ErrorCodes::BadValue, sb.str()); + } } -} // namespace optionenvironment -} // namespace mongo + return Status::OK(); +} + +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/constraints.h b/src/mongo/util/options_parser/constraints.h index 4f9b0244b61..b4a820f983d 100644 --- a/src/mongo/util/options_parser/constraints.h +++ b/src/mongo/util/options_parser/constraints.h @@ -34,149 +34,144 @@ namespace mongo { namespace optionenvironment { - /** A Constraint validates an Environment. It has one function, which takes an Environment as - * an argument and returns either a success or failure Status depending on whether the - * Environment was valid according to this constraint - * - * These are meant to be registered with an Environment to define what it means for that - * Environment to be "valid" and run as part of its validation process - */ - class Constraint { - public: - // Interface - Status operator()(const Environment& env) { return check(env); } - virtual ~Constraint() {} - private: - // Implementation - virtual Status check(const Environment&) = 0; - }; - - /** A KeyConstraint is a Constraint on a specific Key. Currently this is not handled any - * differently than a Constraint, and is only here as a way to help document whether a - * Constraint applies to a single Key or an Environment as a whole - */ - class KeyConstraint : public Constraint { - public: - KeyConstraint(const Key& key) : - _key(key) - { } - virtual ~KeyConstraint() {} - protected: - Key _key; - }; - - /** Implementation of a Constraint on the range of a numeric Value. Fails if the Value is not a - * number, or if it is a number but outside the given range - */ - class NumericKeyConstraint : public KeyConstraint { - public: - NumericKeyConstraint(const Key& k, long min, long max) : - KeyConstraint(k), - _min(min), - _max(max) - { } - virtual ~NumericKeyConstraint() {} - - private: - virtual Status check(const Environment& env); - long _min; - long _max; - }; - - /** Implementation of a Constraint that makes a Value immutable. Fails if the Value has already - * been set and we are attempting to set it to a different Value. Note that setting it to the - * same value is allowed in this implementation - */ - class ImmutableKeyConstraint : public KeyConstraint { - public: - ImmutableKeyConstraint(const Key& k) : KeyConstraint(k) - { } - virtual ~ImmutableKeyConstraint() {} - - private: - virtual Status check(const Environment& env); - Value _value; - }; - - /** Implementation of a Constraint that makes two keys mutually exclusive. Fails if both keys - * are set. - */ - class MutuallyExclusiveKeyConstraint : public KeyConstraint { - public: - MutuallyExclusiveKeyConstraint(const Key& key, const Key& otherKey) : KeyConstraint(key), - _otherKey(otherKey) - { } - virtual ~MutuallyExclusiveKeyConstraint() {} - private: - virtual Status check(const Environment& env); - Key _otherKey; - }; - - /** Implementation of a Constraint that makes one key require another. Fails if the first key - * is set but the other is not. - */ - class RequiresOtherKeyConstraint : public KeyConstraint { - public: - RequiresOtherKeyConstraint(const Key& key, const Key& otherKey) : KeyConstraint(key), - _otherKey(otherKey) - { } - virtual ~RequiresOtherKeyConstraint() {} - private: - virtual Status check(const Environment& env); - Key _otherKey; - }; - - /** Implementation of a Constraint that enforces a specific format on a std::string value. Fails if - * the value of the key is not a std::string or does not match the given regex. - */ - class StringFormatKeyConstraint : public KeyConstraint { - public: - StringFormatKeyConstraint(const Key& key, - const std::string& regexFormat, - const std::string& displayFormat) : KeyConstraint(key), - _regexFormat(regexFormat), - _displayFormat(displayFormat) - { } - virtual ~StringFormatKeyConstraint() {} - private: - virtual Status check(const Environment& env); - std::string _regexFormat; - std::string _displayFormat; - }; - - /** Implementation of a Constraint on the type of a Value. Fails if we cannot extract the given - * type from our Value, which means the implementation of the access functions of Value - * controls which types are "compatible" - */ - template <typename T> - class TypeKeyConstraint : public KeyConstraint { - public: - TypeKeyConstraint(const Key& k) : - KeyConstraint(k) - { } - virtual ~TypeKeyConstraint() {} - - private: - virtual Status check(const Environment& env) { - Value val; - Status s = env.get(_key, &val); - if (!s.isOK()) { - // Key not set, skipping type constraint check - return Status::OK(); - } - - // The code that controls whether a type is "compatible" is contained in the Value - // class, so if that handles compatibility between numeric types then this will too. - T typedVal; - if (!val.get(&typedVal).isOK()) { - StringBuilder sb; - sb << "Error: value for key: " << _key << " was found as type: " - << val.typeToString() << " but is required to be type: " << typeid(typedVal).name(); - return Status(ErrorCodes::InternalError, sb.str()); - } +/** A Constraint validates an Environment. It has one function, which takes an Environment as + * an argument and returns either a success or failure Status depending on whether the + * Environment was valid according to this constraint + * + * These are meant to be registered with an Environment to define what it means for that + * Environment to be "valid" and run as part of its validation process + */ +class Constraint { +public: + // Interface + Status operator()(const Environment& env) { + return check(env); + } + virtual ~Constraint() {} + +private: + // Implementation + virtual Status check(const Environment&) = 0; +}; + +/** A KeyConstraint is a Constraint on a specific Key. Currently this is not handled any + * differently than a Constraint, and is only here as a way to help document whether a + * Constraint applies to a single Key or an Environment as a whole + */ +class KeyConstraint : public Constraint { +public: + KeyConstraint(const Key& key) : _key(key) {} + virtual ~KeyConstraint() {} + +protected: + Key _key; +}; + +/** Implementation of a Constraint on the range of a numeric Value. Fails if the Value is not a + * number, or if it is a number but outside the given range + */ +class NumericKeyConstraint : public KeyConstraint { +public: + NumericKeyConstraint(const Key& k, long min, long max) + : KeyConstraint(k), _min(min), _max(max) {} + virtual ~NumericKeyConstraint() {} + +private: + virtual Status check(const Environment& env); + long _min; + long _max; +}; + +/** Implementation of a Constraint that makes a Value immutable. Fails if the Value has already + * been set and we are attempting to set it to a different Value. Note that setting it to the + * same value is allowed in this implementation + */ +class ImmutableKeyConstraint : public KeyConstraint { +public: + ImmutableKeyConstraint(const Key& k) : KeyConstraint(k) {} + virtual ~ImmutableKeyConstraint() {} + +private: + virtual Status check(const Environment& env); + Value _value; +}; + +/** Implementation of a Constraint that makes two keys mutually exclusive. Fails if both keys + * are set. + */ +class MutuallyExclusiveKeyConstraint : public KeyConstraint { +public: + MutuallyExclusiveKeyConstraint(const Key& key, const Key& otherKey) + : KeyConstraint(key), _otherKey(otherKey) {} + virtual ~MutuallyExclusiveKeyConstraint() {} + +private: + virtual Status check(const Environment& env); + Key _otherKey; +}; + +/** Implementation of a Constraint that makes one key require another. Fails if the first key + * is set but the other is not. + */ +class RequiresOtherKeyConstraint : public KeyConstraint { +public: + RequiresOtherKeyConstraint(const Key& key, const Key& otherKey) + : KeyConstraint(key), _otherKey(otherKey) {} + virtual ~RequiresOtherKeyConstraint() {} + +private: + virtual Status check(const Environment& env); + Key _otherKey; +}; + +/** Implementation of a Constraint that enforces a specific format on a std::string value. Fails if + * the value of the key is not a std::string or does not match the given regex. + */ +class StringFormatKeyConstraint : public KeyConstraint { +public: + StringFormatKeyConstraint(const Key& key, + const std::string& regexFormat, + const std::string& displayFormat) + : KeyConstraint(key), _regexFormat(regexFormat), _displayFormat(displayFormat) {} + virtual ~StringFormatKeyConstraint() {} + +private: + virtual Status check(const Environment& env); + std::string _regexFormat; + std::string _displayFormat; +}; + +/** Implementation of a Constraint on the type of a Value. Fails if we cannot extract the given + * type from our Value, which means the implementation of the access functions of Value + * controls which types are "compatible" + */ +template <typename T> +class TypeKeyConstraint : public KeyConstraint { +public: + TypeKeyConstraint(const Key& k) : KeyConstraint(k) {} + virtual ~TypeKeyConstraint() {} + +private: + virtual Status check(const Environment& env) { + Value val; + Status s = env.get(_key, &val); + if (!s.isOK()) { + // Key not set, skipping type constraint check return Status::OK(); } - }; -} // namespace optionenvironment -} // namespace mongo + // The code that controls whether a type is "compatible" is contained in the Value + // class, so if that handles compatibility between numeric types then this will too. + T typedVal; + if (!val.get(&typedVal).isOK()) { + StringBuilder sb; + sb << "Error: value for key: " << _key << " was found as type: " << val.typeToString() + << " but is required to be type: " << typeid(typedVal).name(); + return Status(ErrorCodes::InternalError, sb.str()); + } + return Status::OK(); + } +}; + +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/environment.cpp b/src/mongo/util/options_parser/environment.cpp index 1178b6e356f..b5a9a61a7e6 100644 --- a/src/mongo/util/options_parser/environment.cpp +++ b/src/mongo/util/options_parser/environment.cpp @@ -38,351 +38,337 @@ namespace mongo { namespace optionenvironment { - using boost::shared_ptr; - using std::string; - using std::type_info; - - // Environment implementation - - Status Environment::addKeyConstraint(KeyConstraint* keyConstraint) { - keyConstraints.push_back(keyConstraint); - return Status::OK(); - } - Status Environment::addConstraint(Constraint* constraint) { - constraints.push_back(constraint); - return Status::OK(); - } - - /** Get the value at Key. Note that we should not be able to add empty values to the - * environment, so we don't check for that here */ - Status Environment::get(const Key& get_key, Value* get_value) const { - typedef std::map<Key, Value>::const_iterator it_type; - it_type value = values.find(get_key); - if (value == values.end()) { - value = default_values.find(get_key); - if (value == default_values.end()) { - StringBuilder sb; - sb << "Value not found for key: " << get_key; - return Status(ErrorCodes::NoSuchKey, sb.str()); - } +using boost::shared_ptr; +using std::string; +using std::type_info; + +// Environment implementation + +Status Environment::addKeyConstraint(KeyConstraint* keyConstraint) { + keyConstraints.push_back(keyConstraint); + return Status::OK(); +} +Status Environment::addConstraint(Constraint* constraint) { + constraints.push_back(constraint); + return Status::OK(); +} + +/** Get the value at Key. Note that we should not be able to add empty values to the + * environment, so we don't check for that here */ +Status Environment::get(const Key& get_key, Value* get_value) const { + typedef std::map<Key, Value>::const_iterator it_type; + it_type value = values.find(get_key); + if (value == values.end()) { + value = default_values.find(get_key); + if (value == default_values.end()) { + StringBuilder sb; + sb << "Value not found for key: " << get_key; + return Status(ErrorCodes::NoSuchKey, sb.str()); } - *get_value = value->second; - return Status::OK(); + } + *get_value = value->second; + return Status::OK(); +} + +/** Set the Value in our Environment. Always disallow empty values */ +Status Environment::set(const Key& add_key, const Value& add_value) { + // 1. Make sure value is not empty + if (add_value.isEmpty()) { + return Status(ErrorCodes::InternalError, "Attempted to add an empty value"); } - /** Set the Value in our Environment. Always disallow empty values */ - Status Environment::set(const Key& add_key, const Value& add_value) { - - // 1. Make sure value is not empty - if (add_value.isEmpty()) { - return Status(ErrorCodes::InternalError, "Attempted to add an empty value"); - } - - // 2. Save old values - std::map <Key, Value> old_values = values; + // 2. Save old values + std::map<Key, Value> old_values = values; - // 3. Add value to be added - values[add_key] = add_value; + // 3. Add value to be added + values[add_key] = add_value; - // 4. Validate only if our environment is already valid - if (valid) { - Status ret = validate(); - if (!ret.isOK()) { - // 5. Revert to old values if this was invalid - values = old_values; - return ret; - } + // 4. Validate only if our environment is already valid + if (valid) { + Status ret = validate(); + if (!ret.isOK()) { + // 5. Revert to old values if this was invalid + values = old_values; + return ret; } - - return Status::OK(); } - /** Removes a Value from our Environment */ - Status Environment::remove(const Key& remove_key) { + return Status::OK(); +} - // 1. Save old values - std::map <Key, Value> old_values = values; +/** Removes a Value from our Environment */ +Status Environment::remove(const Key& remove_key) { + // 1. Save old values + std::map<Key, Value> old_values = values; - // 2. Remove value to be removed - values.erase(remove_key); + // 2. Remove value to be removed + values.erase(remove_key); - // 3. Validate only if our environment is already valid - if (valid) { - Status ret = validate(); - if (!ret.isOK()) { - // 4. Revert to old values if this was invalid - values = old_values; - return ret; - } + // 3. Validate only if our environment is already valid + if (valid) { + Status ret = validate(); + if (!ret.isOK()) { + // 4. Revert to old values if this was invalid + values = old_values; + return ret; } - - return Status::OK(); } - /** Set the default Value for the given Key in our Environment. Always disallow empty values */ - Status Environment::setDefault(const Key& add_key, const Value& add_value) { - - // 1. Make sure value is not empty - if (add_value.isEmpty()) { - return Status(ErrorCodes::InternalError, "Attempted to set an empty default value"); - } - - // 2. Disallow modifying defaults after calling validate on this Environment - if (valid) { - return Status(ErrorCodes::InternalError, - "Attempted to set a default value after calling validate"); - } - - // 3. Add this value to our defaults - default_values[add_key] = add_value; + return Status::OK(); +} - return Status::OK(); +/** Set the default Value for the given Key in our Environment. Always disallow empty values */ +Status Environment::setDefault(const Key& add_key, const Value& add_value) { + // 1. Make sure value is not empty + if (add_value.isEmpty()) { + return Status(ErrorCodes::InternalError, "Attempted to set an empty default value"); } - /** Set all the Values from the source Environment in our Environment. Does not check for empty - * values as the source Environment should not have been allowed to have any */ - Status Environment::setAll(const Environment& add_environment) { + // 2. Disallow modifying defaults after calling validate on this Environment + if (valid) { + return Status(ErrorCodes::InternalError, + "Attempted to set a default value after calling validate"); + } - // 1. Save old values - std::map <Key, Value> old_values = values; + // 3. Add this value to our defaults + default_values[add_key] = add_value; - // 2. Add values to be added - std::map <Key, Value> add_values = add_environment.values; - for (std::map<Key, Value>::const_iterator iterator = add_values.begin(); - iterator != add_values.end(); iterator++) { - values[iterator->first] = iterator->second; - } + return Status::OK(); +} - // 3. Validate only if our environment is already valid - if (valid) { - Status ret = validate(); - if (!ret.isOK()) { - // 4. Revert to old values if this was invalid - values = old_values; - return ret; - } - } +/** Set all the Values from the source Environment in our Environment. Does not check for empty + * values as the source Environment should not have been allowed to have any */ +Status Environment::setAll(const Environment& add_environment) { + // 1. Save old values + std::map<Key, Value> old_values = values; - return Status::OK(); + // 2. Add values to be added + std::map<Key, Value> add_values = add_environment.values; + for (std::map<Key, Value>::const_iterator iterator = add_values.begin(); + iterator != add_values.end(); + iterator++) { + values[iterator->first] = iterator->second; } - /** Validate the Environment by iterating over all our constraints and calling them on our - * Environment - */ - Status Environment::validate(bool setValid) { - - // 1. Iterate and check all KeyConstraints - typedef std::vector<KeyConstraint*>::iterator it_keyConstraint; - for (it_keyConstraint iterator = keyConstraints.begin(); - iterator != keyConstraints.end(); iterator++) { - Status ret = (**iterator)(*this); - if (!ret.isOK()) { - return ret; - } + // 3. Validate only if our environment is already valid + if (valid) { + Status ret = validate(); + if (!ret.isOK()) { + // 4. Revert to old values if this was invalid + values = old_values; + return ret; } + } - // 2. Iterate and check all Constraints - typedef std::vector<Constraint*>::iterator it_constraint; - for (it_constraint iterator = constraints.begin(); - iterator != constraints.end(); iterator++) { - Status ret = (**iterator)(*this); - if (!ret.isOK()) { - return ret; - } - } + return Status::OK(); +} - // 3. Our Environment is now valid. Record this if we should and return success - if (setValid) { - valid = true; +/** Validate the Environment by iterating over all our constraints and calling them on our + * Environment + */ +Status Environment::validate(bool setValid) { + // 1. Iterate and check all KeyConstraints + typedef std::vector<KeyConstraint*>::iterator it_keyConstraint; + for (it_keyConstraint iterator = keyConstraints.begin(); iterator != keyConstraints.end(); + iterator++) { + Status ret = (**iterator)(*this); + if (!ret.isOK()) { + return ret; } - return Status::OK(); } - /** Implementation of legacy interface to be consistent with - * boost::program_options::variables_map during the transition period - * - * boost::program_options::variables_map inherits the count function from std::map, which - * returns 1 if the value is set, and 0 if it is not set - */ - bool Environment::count(const Key& key) const { - Value value; - Status ret = get(key, &value); - if (ret.isOK()) { - return true; - } - else { - return false; + // 2. Iterate and check all Constraints + typedef std::vector<Constraint*>::iterator it_constraint; + for (it_constraint iterator = constraints.begin(); iterator != constraints.end(); iterator++) { + Status ret = (**iterator)(*this); + if (!ret.isOK()) { + return ret; } } - Value Environment::operator[](const Key& key) const { - Value value; - Status ret = get(key, &value); - return value; + // 3. Our Environment is now valid. Record this if we should and return success + if (setValid) { + valid = true; } + return Status::OK(); +} - /* Debugging */ - void Environment::dump() const { - std::map<Key, Value>::const_iterator iter; - for (iter = values.begin(); iter != values.end(); ++iter) { - std::cout << "Key: '" - << iter->first - << "', Value: '" - << iter->second.toString() - << "'" << std::endl; - } +/** Implementation of legacy interface to be consistent with + * boost::program_options::variables_map during the transition period + * + * boost::program_options::variables_map inherits the count function from std::map, which + * returns 1 if the value is set, and 0 if it is not set + */ +bool Environment::count(const Key& key) const { + Value value; + Status ret = get(key, &value); + if (ret.isOK()) { + return true; + } else { + return false; } +} + +Value Environment::operator[](const Key& key) const { + Value value; + Status ret = get(key, &value); + return value; +} + +/* Debugging */ +void Environment::dump() const { + std::map<Key, Value>::const_iterator iter; + for (iter = values.begin(); iter != values.end(); ++iter) { + std::cout << "Key: '" << iter->first << "', Value: '" << iter->second.toString() << "'" + << std::endl; + } +} namespace { - // Converts a map of values with dotted key names to a BSONObj with sub objects. - // 1. Check for dotted field names and call valueMapToBSON recursively. - // 2. Append the actual value to our builder if we did not find a dot in our key name. - Status valueMapToBSON(const std::map<Key, Value>& params, - BSONObjBuilder* builder, - const std::string& prefix = std::string()) { - for (std::map<Key, Value>::const_iterator it(params.begin()); - it != params.end(); it++) { - Key key = it->first; - Value value = it->second; - - // 1. Check for dotted field names and call valueMapToBSON recursively. - // NOTE: this code depends on the fact that std::map is sorted - // - // EXAMPLE: - // The map: - // { - // "var1.dotted1" : false, - // "var2" : true, - // "var1.dotted2" : 6 - // } - // - // Gets sorted by keys as: - // { - // "var1.dotted1" : false, - // "var1.dotted2" : 6, - // "var2" : true - // } - // - // Which means when we see the "var1" prefix, we can iterate until we see either a name - // without a dot or without "var1" as a prefix, aggregating its fields in a new map as - // we go. Because the map is sorted, once we see a name without a dot or a "var1" - // prefix we know that we've seen everything with "var1" as a prefix and can recursively - // build the entire sub object at once using our new map (which is the only way to make - // a single coherent BSON sub object using this append only builder). - // - // The result of this function for this example should be a BSON object of the form: - // { - // "var1" : { - // "dotted1" : false, - // "dotted2" : 6 - // }, - // "var2" : true - // } - - // Check to see if this key name is dotted - std::string::size_type dotOffset = key.find('.'); - if (dotOffset != string::npos) { - - // Get the name of the "section" that we are currently iterating. This will be - // the name of our sub object. - std::string sectionName = key.substr(0, dotOffset); - - // Build a map of the "section" that we are iterating to be passed in a - // recursive call. - std::map<Key, Value> sectionMap; - - std::string beforeDot = key.substr(0, dotOffset); - std::string afterDot = key.substr(dotOffset + 1, key.size() - dotOffset - 1); - std::map<Key, Value>::const_iterator it_next = it; - - do { - // Here we know that the key at it_next has a dot and has the prefix we are - // currently creating a sub object for. Since that means we will definitely - // process that element in this loop, advance the outer for loop iterator here. - it = it_next; - - // Add the value to our section map with a key of whatever is after the dot - // since the section name itself will be part of our sub object builder. - sectionMap[afterDot] = value; - - // Peek at the next value for our iterator and check to see if we've finished. - if (++it_next == params.end()) { - break; - } - key = it_next->first; - value = it_next->second; - - // Look for a dot for our next iteration. - dotOffset = key.find('.'); - - beforeDot = key.substr(0, dotOffset); - afterDot = key.substr(dotOffset + 1, key.size() - dotOffset - 1); +// Converts a map of values with dotted key names to a BSONObj with sub objects. +// 1. Check for dotted field names and call valueMapToBSON recursively. +// 2. Append the actual value to our builder if we did not find a dot in our key name. +Status valueMapToBSON(const std::map<Key, Value>& params, + BSONObjBuilder* builder, + const std::string& prefix = std::string()) { + for (std::map<Key, Value>::const_iterator it(params.begin()); it != params.end(); it++) { + Key key = it->first; + Value value = it->second; + + // 1. Check for dotted field names and call valueMapToBSON recursively. + // NOTE: this code depends on the fact that std::map is sorted + // + // EXAMPLE: + // The map: + // { + // "var1.dotted1" : false, + // "var2" : true, + // "var1.dotted2" : 6 + // } + // + // Gets sorted by keys as: + // { + // "var1.dotted1" : false, + // "var1.dotted2" : 6, + // "var2" : true + // } + // + // Which means when we see the "var1" prefix, we can iterate until we see either a name + // without a dot or without "var1" as a prefix, aggregating its fields in a new map as + // we go. Because the map is sorted, once we see a name without a dot or a "var1" + // prefix we know that we've seen everything with "var1" as a prefix and can recursively + // build the entire sub object at once using our new map (which is the only way to make + // a single coherent BSON sub object using this append only builder). + // + // The result of this function for this example should be a BSON object of the form: + // { + // "var1" : { + // "dotted1" : false, + // "dotted2" : 6 + // }, + // "var2" : true + // } + + // Check to see if this key name is dotted + std::string::size_type dotOffset = key.find('.'); + if (dotOffset != string::npos) { + // Get the name of the "section" that we are currently iterating. This will be + // the name of our sub object. + std::string sectionName = key.substr(0, dotOffset); + + // Build a map of the "section" that we are iterating to be passed in a + // recursive call. + std::map<Key, Value> sectionMap; + + std::string beforeDot = key.substr(0, dotOffset); + std::string afterDot = key.substr(dotOffset + 1, key.size() - dotOffset - 1); + std::map<Key, Value>::const_iterator it_next = it; + + do { + // Here we know that the key at it_next has a dot and has the prefix we are + // currently creating a sub object for. Since that means we will definitely + // process that element in this loop, advance the outer for loop iterator here. + it = it_next; + + // Add the value to our section map with a key of whatever is after the dot + // since the section name itself will be part of our sub object builder. + sectionMap[afterDot] = value; + + // Peek at the next value for our iterator and check to see if we've finished. + if (++it_next == params.end()) { + break; } - while (dotOffset != string::npos && beforeDot == sectionName); + key = it_next->first; + value = it_next->second; - // Use the section name in our object builder, and recursively call - // valueMapToBSON with our sub map with keys that have the section name removed. - BSONObjBuilder sectionObjBuilder(builder->subobjStart(sectionName)); - valueMapToBSON(sectionMap, §ionObjBuilder, sectionName); - sectionObjBuilder.done(); + // Look for a dot for our next iteration. + dotOffset = key.find('.'); - // Our iterator is currently on the last field that matched our dot and prefix, so - // continue to the next loop iteration. - continue; - } + beforeDot = key.substr(0, dotOffset); + afterDot = key.substr(dotOffset + 1, key.size() - dotOffset - 1); + } while (dotOffset != string::npos && beforeDot == sectionName); - // 2. Append the actual value to our builder if we did not find a dot in our key name. - const type_info& type = value.type(); + // Use the section name in our object builder, and recursively call + // valueMapToBSON with our sub map with keys that have the section name removed. + BSONObjBuilder sectionObjBuilder(builder->subobjStart(sectionName)); + valueMapToBSON(sectionMap, §ionObjBuilder, sectionName); + sectionObjBuilder.done(); - if (type == typeid(string)){ - if (value.as<string>().empty()) { - // boost po uses empty string for flags like --quiet - // TODO: Remove this when we remove boost::program_options - builder->appendBool(key, true); - } - else { - builder->append(key, value.as<string>()); - } + // Our iterator is currently on the last field that matched our dot and prefix, so + // continue to the next loop iteration. + continue; + } + + // 2. Append the actual value to our builder if we did not find a dot in our key name. + const type_info& type = value.type(); + + if (type == typeid(string)) { + if (value.as<string>().empty()) { + // boost po uses empty string for flags like --quiet + // TODO: Remove this when we remove boost::program_options + builder->appendBool(key, true); + } else { + builder->append(key, value.as<string>()); } - else if (type == typeid(int)) - builder->append(key, value.as<int>()); - else if (type == typeid(double)) - builder->append(key, value.as<double>()); - else if (type == typeid(bool)) - builder->appendBool(key, value.as<bool>()); - else if (type == typeid(long)) - builder->appendNumber(key, (long long)value.as<long>()); - else if (type == typeid(unsigned)) - builder->appendNumber(key, (long long)value.as<unsigned>()); - else if (type == typeid(unsigned long long)) - builder->appendNumber(key, (long long)value.as<unsigned long long>()); - else if (type == typeid(StringVector_t)) - builder->append(key, value.as<StringVector_t>()); - else if (type == typeid(StringMap_t)) { - BSONObjBuilder subBuilder(builder->subobjStart(key)); - StringMap_t stringMap = value.as<StringMap_t>(); - for (StringMap_t::iterator stringMapIt = stringMap.begin(); - stringMapIt != stringMap.end(); stringMapIt++) { - subBuilder.append(stringMapIt->first, stringMapIt->second); - } - subBuilder.done(); + } else if (type == typeid(int)) + builder->append(key, value.as<int>()); + else if (type == typeid(double)) + builder->append(key, value.as<double>()); + else if (type == typeid(bool)) + builder->appendBool(key, value.as<bool>()); + else if (type == typeid(long)) + builder->appendNumber(key, (long long)value.as<long>()); + else if (type == typeid(unsigned)) + builder->appendNumber(key, (long long)value.as<unsigned>()); + else if (type == typeid(unsigned long long)) + builder->appendNumber(key, (long long)value.as<unsigned long long>()); + else if (type == typeid(StringVector_t)) + builder->append(key, value.as<StringVector_t>()); + else if (type == typeid(StringMap_t)) { + BSONObjBuilder subBuilder(builder->subobjStart(key)); + StringMap_t stringMap = value.as<StringMap_t>(); + for (StringMap_t::iterator stringMapIt = stringMap.begin(); + stringMapIt != stringMap.end(); + stringMapIt++) { + subBuilder.append(stringMapIt->first, stringMapIt->second); } - else - builder->append(key, "UNKNOWN TYPE: " + demangleName(type)); - } - return Status::OK(); + subBuilder.done(); + } else + builder->append(key, "UNKNOWN TYPE: " + demangleName(type)); } -} // namespace - - BSONObj Environment::toBSON() const { - BSONObjBuilder builder; - Status ret = valueMapToBSON(values, &builder); - if (!ret.isOK()) { - return BSONObj(); - } - return builder.obj(); + return Status::OK(); +} +} // namespace + +BSONObj Environment::toBSON() const { + BSONObjBuilder builder; + Status ret = valueMapToBSON(values, &builder); + if (!ret.isOK()) { + return BSONObj(); } + return builder.obj(); +} -} // namespace optionenvironment -} // namespace mongo +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/environment.h b/src/mongo/util/options_parser/environment.h index 8c44eb661d1..2941435b9bb 100644 --- a/src/mongo/util/options_parser/environment.h +++ b/src/mongo/util/options_parser/environment.h @@ -38,206 +38,206 @@ namespace mongo { namespace optionenvironment { - class Constraint; - class KeyConstraint; +class Constraint; +class KeyConstraint; - typedef std::string Key; +typedef std::string Key; - /** An Environment is a map of values that can be validated according to a set of registered - * constraints - * - * Usage overview: - * - * 1. Create an empty Environment - * 2. Add Constraints - * 3. Set Key/Value pairs (will not cause constraints to be triggered) - * 4. Validate (will run all constraints) - * 5. Access - * 6. Set/Modify Key/Value pairs (will run all constraints and reject invalid modifications) - * 7. Access - * - * Since the constraints are run whenever we try to set or modify Key/Value pairs after we - * validate, we have the invariant that the Environment is always valid according to its - * Constraints after validation. Adding new constraints is disallowed after validation. - * - * Usage example: - * - * // Create an empty Environment - * Environment environment; - * - * // Initialize our first Key and Value - * Key key1("key1"); - * Value value1(1); +/** An Environment is a map of values that can be validated according to a set of registered + * constraints + * + * Usage overview: + * + * 1. Create an empty Environment + * 2. Add Constraints + * 3. Set Key/Value pairs (will not cause constraints to be triggered) + * 4. Validate (will run all constraints) + * 5. Access + * 6. Set/Modify Key/Value pairs (will run all constraints and reject invalid modifications) + * 7. Access + * + * Since the constraints are run whenever we try to set or modify Key/Value pairs after we + * validate, we have the invariant that the Environment is always valid according to its + * Constraints after validation. Adding new constraints is disallowed after validation. + * + * Usage example: + * + * // Create an empty Environment + * Environment environment; + * + * // Initialize our first Key and Value + * Key key1("key1"); + * Value value1(1); + * + * // Add a Constraint on "key1" + * Status ret = environment.addConstraint(new ImmutableKeyConstraint("key1")); + * if (!ret.isOK()) { + * return ret; + * } + * + * // Set our first Key and Value to the Environment + * ret = environment.set(key1, value1); + * if (!ret.isOK()) { + * return ret; + * } + * + * // Attempt to mutate should be successful, since validate has not been called + * ret = environment.set(key1, Value(2)); + * if (!ret.isOK()) { + * return ret; + * } + * + * // Validate our Environment + * ret = environment.validate(); + * if (!ret.isOK()) { + * return ret; + * } + * + * // Access our Environment + * int intvalue1; + * ret = environment.get(key1, &intvalue1); + * if (!ret.isOK()) { + * return ret; + * } + * + * // Attempt to mutate should fail, since validate has been called + * ret = environment.set(key1, Value(3)); + * if (!ret.isOK()) { + * return ret; + * } + */ + +class Environment { +public: + Environment() : valid(false) {} + ~Environment() {} + + /** These functions are to add Constraints and KeyConstraints which will be run against + * this environment in the following situations: + * 1. in the "validate" function + * 2. in the "set" function after validate has been called successfully * - * // Add a Constraint on "key1" - * Status ret = environment.addConstraint(new ImmutableKeyConstraint("key1")); - * if (!ret.isOK()) { - * return ret; - * } + * It is an error to call these functions after "validate" has been called * - * // Set our first Key and Value to the Environment - * ret = environment.set(key1, value1); - * if (!ret.isOK()) { - * return ret; - * } + * NOTE: These DO NOT take ownership of the pointer passed in + */ + Status addKeyConstraint(KeyConstraint* keyConstraint); + Status addConstraint(Constraint* constraint); + + /** Add the Value to this Environment with the given Key. If "validate" has already + * been called on this Environment, runs all Constraints on the new Environment. If + * any of the Constraints fail, reverts to the old Environment and returns an error + */ + Status set(const Key& key, const Value& value); + + /** Remove the Value from this Environment with the given Key. If "validate" has + * already been called on this Environment, runs all Constraints on the new Environment. + * If any of the Constraints fail, reverts to the old Environment and returns an error + */ + Status remove(const Key& key); + + /** Add a default Value to this Environment with the given Key. Fails if validate has + * already been called on our environment. The get functions will return the default + * if one exists and the value has not been explicitly set. + */ + Status setDefault(const Key& key, const Value& value); + + /** Populate the given Value with the Value stored for the given Key. Return a success + * status if the value was found, or an error status if the value was not found. + * Leaves the Value unchanged on error. + */ + Status get(const Key& key, Value* value) const; + + /** Same as the above get interface, but supports directly getting C++ types without the + * intermediate Value and has the added failure case of the value being the wrong type + */ + template <typename T> + Status get(const Key& key, T* value_contents) const; + + /** Runs all registered Constraints and returns the result. If "setValid" is true and + * validation succeeds, marks this as a valid Environment so that any modifications will + * re run all Constraints + */ + Status validate(bool setValid = true); + + /** Sets all variables in the given Environment in this Environment. Does not add + * Constraints + */ + Status setAll(const Environment& other); + + /** The functions below are the legacy interface to be consistent with + * boost::program_options::variables_map during the transition period + */ + + /** + * @return 1 if the given Key has a Value set in this Environment and 0 if not + */ + bool count(const Key& key) const; + + /** + * @return the Value for the given Key in this Environment. Returns an empty Value if + * Key is not set. + */ + Value operator[](const Key& key) const; + + /** + * Gets the BSON representation of this Environment. This will collapse dotted fields + * into sub objects. * - * // Attempt to mutate should be successful, since validate has not been called - * ret = environment.set(key1, Value(2)); - * if (!ret.isOK()) { - * return ret; - * } + * Example: * - * // Validate our Environment - * ret = environment.validate(); - * if (!ret.isOK()) { - * return ret; - * } + * The following Environment values map: + * "a.b.c" -> true + * "a.b.d" -> false + * "a.e.f" -> 0 + * "a.e.g" -> 1 + * "a.h" -> "foo" * - * // Access our Environment - * int intvalue1; - * ret = environment.get(key1, &intvalue1); - * if (!ret.isOK()) { - * return ret; + * Has a BSON represation of (shown as JSON): + * { "a" : { + * "b" : { + * "c" : true, + * "d" : false + * }, + * "e" : { + * "f" : 0, + * "g" : 1 + * }, + * "h" : "foo" + * } * } * - * // Attempt to mutate should fail, since validate has been called - * ret = environment.set(key1, Value(3)); - * if (!ret.isOK()) { - * return ret; - * } + * Note that the BSON representation only includes fields that were explicitly set using + * setAll or set, and not defaults that were specified using setDefault. */ - - class Environment { - public: - Environment() : valid(false) { } - ~Environment() { } - - /** These functions are to add Constraints and KeyConstraints which will be run against - * this environment in the following situations: - * 1. in the "validate" function - * 2. in the "set" function after validate has been called successfully - * - * It is an error to call these functions after "validate" has been called - * - * NOTE: These DO NOT take ownership of the pointer passed in - */ - Status addKeyConstraint(KeyConstraint* keyConstraint); - Status addConstraint(Constraint* constraint); - - /** Add the Value to this Environment with the given Key. If "validate" has already - * been called on this Environment, runs all Constraints on the new Environment. If - * any of the Constraints fail, reverts to the old Environment and returns an error - */ - Status set(const Key& key, const Value& value); - - /** Remove the Value from this Environment with the given Key. If "validate" has - * already been called on this Environment, runs all Constraints on the new Environment. - * If any of the Constraints fail, reverts to the old Environment and returns an error - */ - Status remove(const Key& key); - - /** Add a default Value to this Environment with the given Key. Fails if validate has - * already been called on our environment. The get functions will return the default - * if one exists and the value has not been explicitly set. - */ - Status setDefault(const Key& key, const Value& value); - - /** Populate the given Value with the Value stored for the given Key. Return a success - * status if the value was found, or an error status if the value was not found. - * Leaves the Value unchanged on error. - */ - Status get(const Key& key, Value* value) const; - - /** Same as the above get interface, but supports directly getting C++ types without the - * intermediate Value and has the added failure case of the value being the wrong type - */ - template <typename T> - Status get(const Key& key, T* value_contents) const; - - /** Runs all registered Constraints and returns the result. If "setValid" is true and - * validation succeeds, marks this as a valid Environment so that any modifications will - * re run all Constraints - */ - Status validate(bool setValid=true); - - /** Sets all variables in the given Environment in this Environment. Does not add - * Constraints - */ - Status setAll(const Environment& other); - - /** The functions below are the legacy interface to be consistent with - * boost::program_options::variables_map during the transition period - */ - - /** - * @return 1 if the given Key has a Value set in this Environment and 0 if not - */ - bool count(const Key& key) const; - - /** - * @return the Value for the given Key in this Environment. Returns an empty Value if - * Key is not set. - */ - Value operator[](const Key& key) const; - - /** - * Gets the BSON representation of this Environment. This will collapse dotted fields - * into sub objects. - * - * Example: - * - * The following Environment values map: - * "a.b.c" -> true - * "a.b.d" -> false - * "a.e.f" -> 0 - * "a.e.g" -> 1 - * "a.h" -> "foo" - * - * Has a BSON represation of (shown as JSON): - * { "a" : { - * "b" : { - * "c" : true, - * "d" : false - * }, - * "e" : { - * "f" : 0, - * "g" : 1 - * }, - * "h" : "foo" - * } - * } - * - * Note that the BSON representation only includes fields that were explicitly set using - * setAll or set, and not defaults that were specified using setDefault. - */ - BSONObj toBSON() const; - - /* Debugging */ - void dump() const; - - protected: - std::vector<Constraint*> constraints; - std::vector<KeyConstraint*> keyConstraints; - std::map <Key, Value> values; - std::map <Key, Value> default_values; - bool valid; - }; - - template <typename T> - Status Environment::get(const Key& get_key, T* get_value) const { - Value value; - Status ret = get(get_key, &value); - if (!ret.isOK()) { - return ret; - } - ret = value.get(get_value); - if (!ret.isOK()) { - StringBuilder sb; - sb << "Error getting value for key: \"" << get_key << "\": " << ret.toString(); - return Status(ErrorCodes::NoSuchKey, sb.str()); - } - return Status::OK(); + BSONObj toBSON() const; + + /* Debugging */ + void dump() const; + +protected: + std::vector<Constraint*> constraints; + std::vector<KeyConstraint*> keyConstraints; + std::map<Key, Value> values; + std::map<Key, Value> default_values; + bool valid; +}; + +template <typename T> +Status Environment::get(const Key& get_key, T* get_value) const { + Value value; + Status ret = get(get_key, &value); + if (!ret.isOK()) { + return ret; + } + ret = value.get(get_value); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Error getting value for key: \"" << get_key << "\": " << ret.toString(); + return Status(ErrorCodes::NoSuchKey, sb.str()); } + return Status::OK(); +} -} // namespace optionenvironment -} // namespace mongo +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/environment_test.cpp b/src/mongo/util/options_parser/environment_test.cpp index 20c54466b6f..e87267c013e 100644 --- a/src/mongo/util/options_parser/environment_test.cpp +++ b/src/mongo/util/options_parser/environment_test.cpp @@ -33,140 +33,139 @@ namespace { - using mongo::ErrorCodes; - using mongo::Status; - - namespace moe = mongo::optionenvironment; - - TEST(Environment, EmptyValue) { - moe::Environment environment; - ASSERT_NOT_OK(environment.set(moe::Key("empty"), moe::Value())); - } - - TEST(Environment, Immutable) { - moe::Environment environment; - moe::ImmutableKeyConstraint immutableKeyConstraint(moe::Key("port")); - environment.addKeyConstraint(&immutableKeyConstraint); - ASSERT_OK(environment.set(moe::Key("port"), moe::Value(5))); - ASSERT_OK(environment.validate()); - ASSERT_NOT_OK(environment.set(moe::Key("port"), moe::Value(0))); - } - - TEST(Environment, OutOfRange) { - moe::Environment environment; - moe::NumericKeyConstraint numericKeyConstraint(moe::Key("port"), 1000, 65535); - environment.addKeyConstraint(&numericKeyConstraint); - ASSERT_OK(environment.validate()); - ASSERT_NOT_OK(environment.set(moe::Key("port"), moe::Value(0))); - } - - TEST(Environment, NonNumericRangeConstraint) { - moe::Environment environment; - moe::NumericKeyConstraint numericKeyConstraint(moe::Key("port"), 1000, 65535); - environment.addKeyConstraint(&numericKeyConstraint); - ASSERT_OK(environment.validate()); - ASSERT_NOT_OK(environment.set(moe::Key("port"), moe::Value("string"))); - } - - TEST(Environment, BadType) { - moe::Environment environment; - moe::TypeKeyConstraint<int> typeKeyConstraintInt(moe::Key("port")); - environment.addKeyConstraint(&typeKeyConstraintInt); - ASSERT_OK(environment.set(moe::Key("port"), moe::Value("string"))); - ASSERT_NOT_OK(environment.validate()); - } - - TEST(Environment, AllowNumeric) { - moe::Environment environment; - moe::TypeKeyConstraint<long> typeKeyConstraintLong(moe::Key("port")); - environment.addKeyConstraint(&typeKeyConstraintLong); - moe::TypeKeyConstraint<int> typeKeyConstraintInt(moe::Key("port")); - environment.addKeyConstraint(&typeKeyConstraintInt); - ASSERT_OK(environment.set(moe::Key("port"), moe::Value(1))); - ASSERT_OK(environment.validate()); - } - - TEST(Environment, MutuallyExclusive) { - moe::Environment environment; - moe::MutuallyExclusiveKeyConstraint constraint(moe::Key("key"),moe::Key("otherKey")); - environment.addKeyConstraint(&constraint); - ASSERT_OK(environment.set(moe::Key("key"), moe::Value(1))); - ASSERT_OK(environment.set(moe::Key("otherKey"), moe::Value(1))); - ASSERT_NOT_OK(environment.validate()); - } - - TEST(Environment, RequiresOther) { - moe::Environment environment; - moe::RequiresOtherKeyConstraint constraint(moe::Key("key"),moe::Key("otherKey")); - environment.addKeyConstraint(&constraint); - ASSERT_OK(environment.set(moe::Key("key"), moe::Value(1))); - ASSERT_NOT_OK(environment.validate()); - ASSERT_OK(environment.set(moe::Key("otherKey"), moe::Value(1))); - ASSERT_OK(environment.validate()); - } - - TEST(Environment, StringFormat) { - moe::Environment environment; - moe::StringFormatKeyConstraint constraint(moe::Key("key"), "[0-9]","[0-9]"); - environment.addKeyConstraint(&constraint); - ASSERT_OK(environment.set(moe::Key("key"), moe::Value(1))); - ASSERT_NOT_OK(environment.validate()); - ASSERT_OK(environment.set(moe::Key("key"), moe::Value(std::string("a")))); - ASSERT_NOT_OK(environment.validate()); - ASSERT_OK(environment.set(moe::Key("key"), moe::Value(std::string("11")))); - ASSERT_NOT_OK(environment.validate()); - ASSERT_OK(environment.set(moe::Key("key"), moe::Value(std::string("1")))); - ASSERT_OK(environment.validate()); - } - - TEST(Environment, DirectTypeAccess) { - moe::Environment environment; - ASSERT_OK(environment.set(moe::Key("number"), moe::Value(5))); - std::string notNumber; - ASSERT_NOT_OK(environment.get(moe::Key("number"), ¬Number)); - int number; - ASSERT_OK(environment.get(moe::Key("number"), &number)); - ASSERT_EQUALS(number, 5); - } - - TEST(ToBSONTests, NormalValues) { - moe::Environment environment; - ASSERT_OK(environment.set(moe::Key("val1"), moe::Value(6))); - ASSERT_OK(environment.set(moe::Key("val2"), moe::Value(std::string("string")))); - mongo::BSONObj obj = BSON( "val1" << 6 << "val2" << "string" ); - // TODO: Put a comparison here that doesn't depend on the field order. Right now it is - // based on the sort order of keys in a std::map. - ASSERT_EQUALS(obj, environment.toBSON()); - } - - TEST(ToBSONTests, DottedValues) { - moe::Environment environment; - ASSERT_OK(environment.set(moe::Key("val1.dotted1"), moe::Value(6))); - ASSERT_OK(environment.set(moe::Key("val2"), moe::Value(true))); - ASSERT_OK(environment.set(moe::Key("val1.dotted2"), moe::Value(std::string("string")))); - mongo::BSONObj obj = BSON("val1" << BSON( "dotted1" << 6 << "dotted2" << "string") << - "val2" << true ); - // TODO: Put a comparison here that doesn't depend on the field order. Right now it is - // based on the sort order of keys in a std::map. - ASSERT_EQUALS(obj, environment.toBSON()); - } - - TEST(ToBSONTests, DeepDottedValues) { - moe::Environment environment; - ASSERT_OK(environment.set(moe::Key("val1.first1.second1.third1"), moe::Value(6))); - ASSERT_OK(environment.set(moe::Key("val1.first1.second2.third1"), moe::Value(false))); - ASSERT_OK(environment.set(moe::Key("val1.first2"), moe::Value(std::string("string")))); - ASSERT_OK(environment.set(moe::Key("val1.first1.second1.third2"), moe::Value(true))); - ASSERT_OK(environment.set(moe::Key("val2"), moe::Value(6.0))); - mongo::BSONObj obj = BSON("val1" << BSON("first1" << - BSON("second1" << - BSON("third1" << 6 << "third2" << true) << - "second2" << - BSON("third1" << false)) << - "first2" << "string") << - "val2" << 6.0); - // TODO: Put a comparison here that doesn't depend on the field order. Right now it is - // based on the sort order of keys in a std::map. - ASSERT_EQUALS(obj, environment.toBSON()); - } -} // unnamed namespace +using mongo::ErrorCodes; +using mongo::Status; + +namespace moe = mongo::optionenvironment; + +TEST(Environment, EmptyValue) { + moe::Environment environment; + ASSERT_NOT_OK(environment.set(moe::Key("empty"), moe::Value())); +} + +TEST(Environment, Immutable) { + moe::Environment environment; + moe::ImmutableKeyConstraint immutableKeyConstraint(moe::Key("port")); + environment.addKeyConstraint(&immutableKeyConstraint); + ASSERT_OK(environment.set(moe::Key("port"), moe::Value(5))); + ASSERT_OK(environment.validate()); + ASSERT_NOT_OK(environment.set(moe::Key("port"), moe::Value(0))); +} + +TEST(Environment, OutOfRange) { + moe::Environment environment; + moe::NumericKeyConstraint numericKeyConstraint(moe::Key("port"), 1000, 65535); + environment.addKeyConstraint(&numericKeyConstraint); + ASSERT_OK(environment.validate()); + ASSERT_NOT_OK(environment.set(moe::Key("port"), moe::Value(0))); +} + +TEST(Environment, NonNumericRangeConstraint) { + moe::Environment environment; + moe::NumericKeyConstraint numericKeyConstraint(moe::Key("port"), 1000, 65535); + environment.addKeyConstraint(&numericKeyConstraint); + ASSERT_OK(environment.validate()); + ASSERT_NOT_OK(environment.set(moe::Key("port"), moe::Value("string"))); +} + +TEST(Environment, BadType) { + moe::Environment environment; + moe::TypeKeyConstraint<int> typeKeyConstraintInt(moe::Key("port")); + environment.addKeyConstraint(&typeKeyConstraintInt); + ASSERT_OK(environment.set(moe::Key("port"), moe::Value("string"))); + ASSERT_NOT_OK(environment.validate()); +} + +TEST(Environment, AllowNumeric) { + moe::Environment environment; + moe::TypeKeyConstraint<long> typeKeyConstraintLong(moe::Key("port")); + environment.addKeyConstraint(&typeKeyConstraintLong); + moe::TypeKeyConstraint<int> typeKeyConstraintInt(moe::Key("port")); + environment.addKeyConstraint(&typeKeyConstraintInt); + ASSERT_OK(environment.set(moe::Key("port"), moe::Value(1))); + ASSERT_OK(environment.validate()); +} + +TEST(Environment, MutuallyExclusive) { + moe::Environment environment; + moe::MutuallyExclusiveKeyConstraint constraint(moe::Key("key"), moe::Key("otherKey")); + environment.addKeyConstraint(&constraint); + ASSERT_OK(environment.set(moe::Key("key"), moe::Value(1))); + ASSERT_OK(environment.set(moe::Key("otherKey"), moe::Value(1))); + ASSERT_NOT_OK(environment.validate()); +} + +TEST(Environment, RequiresOther) { + moe::Environment environment; + moe::RequiresOtherKeyConstraint constraint(moe::Key("key"), moe::Key("otherKey")); + environment.addKeyConstraint(&constraint); + ASSERT_OK(environment.set(moe::Key("key"), moe::Value(1))); + ASSERT_NOT_OK(environment.validate()); + ASSERT_OK(environment.set(moe::Key("otherKey"), moe::Value(1))); + ASSERT_OK(environment.validate()); +} + +TEST(Environment, StringFormat) { + moe::Environment environment; + moe::StringFormatKeyConstraint constraint(moe::Key("key"), "[0-9]", "[0-9]"); + environment.addKeyConstraint(&constraint); + ASSERT_OK(environment.set(moe::Key("key"), moe::Value(1))); + ASSERT_NOT_OK(environment.validate()); + ASSERT_OK(environment.set(moe::Key("key"), moe::Value(std::string("a")))); + ASSERT_NOT_OK(environment.validate()); + ASSERT_OK(environment.set(moe::Key("key"), moe::Value(std::string("11")))); + ASSERT_NOT_OK(environment.validate()); + ASSERT_OK(environment.set(moe::Key("key"), moe::Value(std::string("1")))); + ASSERT_OK(environment.validate()); +} + +TEST(Environment, DirectTypeAccess) { + moe::Environment environment; + ASSERT_OK(environment.set(moe::Key("number"), moe::Value(5))); + std::string notNumber; + ASSERT_NOT_OK(environment.get(moe::Key("number"), ¬Number)); + int number; + ASSERT_OK(environment.get(moe::Key("number"), &number)); + ASSERT_EQUALS(number, 5); +} + +TEST(ToBSONTests, NormalValues) { + moe::Environment environment; + ASSERT_OK(environment.set(moe::Key("val1"), moe::Value(6))); + ASSERT_OK(environment.set(moe::Key("val2"), moe::Value(std::string("string")))); + mongo::BSONObj obj = BSON("val1" << 6 << "val2" + << "string"); + // TODO: Put a comparison here that doesn't depend on the field order. Right now it is + // based on the sort order of keys in a std::map. + ASSERT_EQUALS(obj, environment.toBSON()); +} + +TEST(ToBSONTests, DottedValues) { + moe::Environment environment; + ASSERT_OK(environment.set(moe::Key("val1.dotted1"), moe::Value(6))); + ASSERT_OK(environment.set(moe::Key("val2"), moe::Value(true))); + ASSERT_OK(environment.set(moe::Key("val1.dotted2"), moe::Value(std::string("string")))); + mongo::BSONObj obj = BSON("val1" << BSON("dotted1" << 6 << "dotted2" + << "string") << "val2" << true); + // TODO: Put a comparison here that doesn't depend on the field order. Right now it is + // based on the sort order of keys in a std::map. + ASSERT_EQUALS(obj, environment.toBSON()); +} + +TEST(ToBSONTests, DeepDottedValues) { + moe::Environment environment; + ASSERT_OK(environment.set(moe::Key("val1.first1.second1.third1"), moe::Value(6))); + ASSERT_OK(environment.set(moe::Key("val1.first1.second2.third1"), moe::Value(false))); + ASSERT_OK(environment.set(moe::Key("val1.first2"), moe::Value(std::string("string")))); + ASSERT_OK(environment.set(moe::Key("val1.first1.second1.third2"), moe::Value(true))); + ASSERT_OK(environment.set(moe::Key("val2"), moe::Value(6.0))); + mongo::BSONObj obj = + BSON("val1" << BSON("first1" << BSON("second1" << BSON("third1" << 6 << "third2" << true) + << "second2" << BSON("third1" << false)) + << "first2" + << "string") << "val2" << 6.0); + // TODO: Put a comparison here that doesn't depend on the field order. Right now it is + // based on the sort order of keys in a std::map. + ASSERT_EQUALS(obj, environment.toBSON()); +} +} // unnamed namespace diff --git a/src/mongo/util/options_parser/option_description.cpp b/src/mongo/util/options_parser/option_description.cpp index c903a3ddce0..d863a614604 100644 --- a/src/mongo/util/options_parser/option_description.cpp +++ b/src/mongo/util/options_parser/option_description.cpp @@ -35,296 +35,275 @@ namespace mongo { namespace optionenvironment { - using boost::shared_ptr; - - 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()); - } - } +using boost::shared_ptr; + +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); } - } // namespace - - OptionDescription::OptionDescription(const std::string& dottedName, - const std::string& singleName, - const OptionType type, - const std::string& description) - : _dottedName(dottedName), - _singleName(singleName), - _type(type), - _description(description), - _isVisible(true), - _default(Value()), - _implicit(Value()), - _isComposing(false), - _sources(SourceAll), - _positionalStart(-1), - _positionalEnd(-1), - _constraints(), - _deprecatedDottedNames() { } - - OptionDescription::OptionDescription(const std::string& dottedName, - const std::string& singleName, - const OptionType type, - const std::string& description, - const std::vector<std::string>& deprecatedDottedNames) - : _dottedName(dottedName), - _singleName(singleName), - _type(type), - _description(description), - _isVisible(true), - _default(Value()), - _implicit(Value()), - _isComposing(false), - _sources(SourceAll), - _positionalStart(-1), - _positionalEnd(-1), - _constraints(), - _deprecatedDottedNames(deprecatedDottedNames) { - - // Verify deprecated dotted names. - // No empty deprecated dotted names. - if (std::count(_deprecatedDottedNames.begin(), _deprecatedDottedNames.end(), "")) { - StringBuilder sb; - sb << "Attempted to register option with empty string for deprecated dotted name"; - throw DBException(sb.str(), ErrorCodes::BadValue); + case Bool: { + bool valueType; + return value.get(&valueType); } - // Should not be the same as _dottedName. - if (std::count(_deprecatedDottedNames.begin(), _deprecatedDottedNames.end(), dottedName)) { - StringBuilder sb; - sb << "Attempted to register option with conflict between dottedName and deprecated " - << "dotted name: " << _dottedName; - throw DBException(sb.str(), ErrorCodes::BadValue); + case Double: { + double valueType; + return value.get(&valueType); } - } - - OptionDescription& OptionDescription::hidden() { - _isVisible = false; - return *this; - } - - OptionDescription& OptionDescription::setDefault(Value defaultValue) { - - // 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 (_isComposing) { - StringBuilder sb; - sb << "Could not register option \"" << _dottedName << "\": " - << "Cannot register a default value for a composing option"; - throw DBException(sb.str(), ErrorCodes::InternalError); + case Int: { + int valueType; + return value.get(&valueType); } - - // Make sure the type of our default value matches our declared type - Status ret = checkValueType(_type, defaultValue); - if (!ret.isOK()) { - StringBuilder sb; - sb << "Could not register option \"" << _dottedName << "\": " - << "mismatch between declared type and type of default value: " - << ret.toString(); - throw DBException(sb.str(), ErrorCodes::InternalError); + case Long: { + long valueType; + return value.get(&valueType); } - - // It doesn't make sense to set a "default value" for switch options, so disallow it here - if (_type == Switch) { - StringBuilder sb; - sb << "Could not register option \"" << _dottedName << "\": " - << "the default value of a Switch option is false and cannot be changed"; - throw DBException(sb.str(), ErrorCodes::InternalError); + case String: { + std::string valueType; + return value.get(&valueType); } - - _default = defaultValue; - return *this; - } - - OptionDescription& OptionDescription::setImplicit(Value implicitValue) { - - // Disallow registering an implicit value for a composing option since the interaction - // between the two is unclear - if (_isComposing) { - StringBuilder sb; - sb << "Could not register option \"" << _dottedName << "\": " - << "Cannot register an implicit value for a composing option"; - throw DBException(sb.str(), ErrorCodes::InternalError); + case UnsignedLongLong: { + unsigned long long valueType; + return value.get(&valueType); } - - // Make sure the type of our implicit value matches our declared type - Status ret = checkValueType(_type, implicitValue); - if (!ret.isOK()) { - StringBuilder sb; - sb << "Could not register option \"" << _dottedName << "\": " - << "mismatch between declared type and type of implicit value: " - << ret.toString(); - throw DBException(sb.str(), ErrorCodes::InternalError); + case Unsigned: { + unsigned valueType; + return value.get(&valueType); } - - // It doesn't make sense to set an "implicit value" for switch options since they can never - // have an argument anyway, so disallow it here - if (_type == Switch) { + case Switch: { + bool valueType; + return value.get(&valueType); + } + default: { StringBuilder sb; - sb << "Could not register option \"" << _dottedName << "\": " - << "the implicit value of a Switch option is true and cannot be changed"; - throw DBException(sb.str(), ErrorCodes::InternalError); + sb << "Unrecognized option type: " << type; + return Status(ErrorCodes::InternalError, sb.str()); } - - _implicit = implicitValue; - return *this; + } +} +} // namespace + +OptionDescription::OptionDescription(const std::string& dottedName, + const std::string& singleName, + const OptionType type, + const std::string& description) + : _dottedName(dottedName), + _singleName(singleName), + _type(type), + _description(description), + _isVisible(true), + _default(Value()), + _implicit(Value()), + _isComposing(false), + _sources(SourceAll), + _positionalStart(-1), + _positionalEnd(-1), + _constraints(), + _deprecatedDottedNames() {} + +OptionDescription::OptionDescription(const std::string& dottedName, + const std::string& singleName, + const OptionType type, + const std::string& description, + const std::vector<std::string>& deprecatedDottedNames) + : _dottedName(dottedName), + _singleName(singleName), + _type(type), + _description(description), + _isVisible(true), + _default(Value()), + _implicit(Value()), + _isComposing(false), + _sources(SourceAll), + _positionalStart(-1), + _positionalEnd(-1), + _constraints(), + _deprecatedDottedNames(deprecatedDottedNames) { + // Verify deprecated dotted names. + // No empty deprecated dotted names. + if (std::count(_deprecatedDottedNames.begin(), _deprecatedDottedNames.end(), "")) { + StringBuilder sb; + sb << "Attempted to register option with empty string for deprecated dotted name"; + throw DBException(sb.str(), ErrorCodes::BadValue); + } + // Should not be the same as _dottedName. + if (std::count(_deprecatedDottedNames.begin(), _deprecatedDottedNames.end(), dottedName)) { + StringBuilder sb; + sb << "Attempted to register option with conflict between dottedName and deprecated " + << "dotted name: " << _dottedName; + throw DBException(sb.str(), ErrorCodes::BadValue); + } +} + +OptionDescription& OptionDescription::hidden() { + _isVisible = false; + return *this; +} + +OptionDescription& OptionDescription::setDefault(Value defaultValue) { + // 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 (_isComposing) { + StringBuilder sb; + sb << "Could not register option \"" << _dottedName << "\": " + << "Cannot register a default value for a composing option"; + throw DBException(sb.str(), ErrorCodes::InternalError); } - OptionDescription& OptionDescription::composing() { - - if (_type != StringVector && _type != StringMap) { - StringBuilder sb; - sb << "Could not register option \"" << _dottedName << "\": " - << "only options registered as StringVector or StringMap can be composing"; - throw DBException(sb.str(), ErrorCodes::InternalError); - } + // Make sure the type of our default value matches our declared type + Status ret = checkValueType(_type, defaultValue); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Could not register option \"" << _dottedName << "\": " + << "mismatch between declared type and type of default value: " << ret.toString(); + throw DBException(sb.str(), ErrorCodes::InternalError); + } - // Disallow registering a default value for a composing option since the interaction - // between the two is unclear - if (!_default.isEmpty()) { - StringBuilder sb; - sb << "Could not register option \"" << _dottedName << "\": " - << "Cannot make an option with an default value composing"; - throw DBException(sb.str(), ErrorCodes::InternalError); - } + // It doesn't make sense to set a "default value" for switch options, so disallow it here + if (_type == Switch) { + StringBuilder sb; + sb << "Could not register option \"" << _dottedName << "\": " + << "the default value of a Switch option is false and cannot be changed"; + throw DBException(sb.str(), ErrorCodes::InternalError); + } - // Disallow registering an implicit value for a composing option since the interaction - // between the two is unclear - if (!_implicit.isEmpty()) { - StringBuilder sb; - sb << "Could not register option \"" << _dottedName << "\": " - << "Cannot make an option with an implicit value composing"; - throw DBException(sb.str(), ErrorCodes::InternalError); - } + _default = defaultValue; + return *this; +} + +OptionDescription& OptionDescription::setImplicit(Value implicitValue) { + // Disallow registering an implicit value for a composing option since the interaction + // between the two is unclear + if (_isComposing) { + StringBuilder sb; + sb << "Could not register option \"" << _dottedName << "\": " + << "Cannot register an implicit value for a composing option"; + throw DBException(sb.str(), ErrorCodes::InternalError); + } - _isComposing = true; - return *this; + // Make sure the type of our implicit value matches our declared type + Status ret = checkValueType(_type, implicitValue); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Could not register option \"" << _dottedName << "\": " + << "mismatch between declared type and type of implicit value: " << ret.toString(); + throw DBException(sb.str(), ErrorCodes::InternalError); } - OptionDescription& OptionDescription::setSources(OptionSources sources) { - _sources = sources; - return *this; + // It doesn't make sense to set an "implicit value" for switch options since they can never + // have an argument anyway, so disallow it here + if (_type == Switch) { + StringBuilder sb; + sb << "Could not register option \"" << _dottedName << "\": " + << "the implicit value of a Switch option is true and cannot be changed"; + throw DBException(sb.str(), ErrorCodes::InternalError); } - OptionDescription& OptionDescription::positional(int start, int end) { + _implicit = implicitValue; + return *this; +} - if (start < 1 || (end < 1 && end != -1) || (end != -1 && end < start)) { - StringBuilder sb; - sb << "Could not register option \"" << _dottedName << "\": " - << "Invalid positional specification: \"start\": " << start << ", \"end\": " << end; - throw DBException(sb.str(), ErrorCodes::InternalError); - } +OptionDescription& OptionDescription::composing() { + if (_type != StringVector && _type != StringMap) { + StringBuilder sb; + sb << "Could not register option \"" << _dottedName << "\": " + << "only options registered as StringVector or StringMap can be composing"; + throw DBException(sb.str(), ErrorCodes::InternalError); + } - if ((end - start) > 0) { - if (_type != StringVector) { - StringBuilder sb; - sb << "Could not register option \"" << _dottedName << "\": " - << "Positional range implies that multiple values are allowed, " - << "but option is not registered as type StringVector"; - throw DBException(sb.str(), ErrorCodes::InternalError); - } - } + // Disallow registering a default value for a composing option since the interaction + // between the two is unclear + if (!_default.isEmpty()) { + StringBuilder sb; + sb << "Could not register option \"" << _dottedName << "\": " + << "Cannot make an option with an default value composing"; + throw DBException(sb.str(), ErrorCodes::InternalError); + } - _positionalStart = start; - _positionalEnd = end; - return *this; + // Disallow registering an implicit value for a composing option since the interaction + // between the two is unclear + if (!_implicit.isEmpty()) { + StringBuilder sb; + sb << "Could not register option \"" << _dottedName << "\": " + << "Cannot make an option with an implicit value composing"; + throw DBException(sb.str(), ErrorCodes::InternalError); } - OptionDescription& OptionDescription::addConstraint(Constraint* c) { - _constraints.push_back(boost::shared_ptr<Constraint>(c)); - return *this; + _isComposing = true; + return *this; +} + +OptionDescription& OptionDescription::setSources(OptionSources sources) { + _sources = sources; + return *this; +} + +OptionDescription& OptionDescription::positional(int start, int end) { + if (start < 1 || (end < 1 && end != -1) || (end != -1 && end < start)) { + StringBuilder sb; + sb << "Could not register option \"" << _dottedName << "\": " + << "Invalid positional specification: \"start\": " << start << ", \"end\": " << end; + throw DBException(sb.str(), ErrorCodes::InternalError); } - OptionDescription& OptionDescription::validRange(long min, long max) { - if (_type != Double && - _type != Int && - _type != Long && - _type != UnsignedLongLong && - _type != Unsigned) { + if ((end - start) > 0) { + if (_type != StringVector) { StringBuilder sb; sb << "Could not register option \"" << _dottedName << "\": " - << "only options registered as a numeric type can have a valid range, " - << "but option has type: " << _type; + << "Positional range implies that multiple values are allowed, " + << "but option is not registered as type StringVector"; throw DBException(sb.str(), ErrorCodes::InternalError); } - - return addConstraint(new NumericKeyConstraint(_dottedName, min, max)); } - OptionDescription& OptionDescription::incompatibleWith(const std::string& otherDottedName) { - return addConstraint(new MutuallyExclusiveKeyConstraint(_dottedName, otherDottedName)); + _positionalStart = start; + _positionalEnd = end; + return *this; +} + +OptionDescription& OptionDescription::addConstraint(Constraint* c) { + _constraints.push_back(boost::shared_ptr<Constraint>(c)); + return *this; +} + +OptionDescription& OptionDescription::validRange(long min, long max) { + if (_type != Double && _type != Int && _type != Long && _type != UnsignedLongLong && + _type != Unsigned) { + StringBuilder sb; + sb << "Could not register option \"" << _dottedName << "\": " + << "only options registered as a numeric type can have a valid range, " + << "but option has type: " << _type; + throw DBException(sb.str(), ErrorCodes::InternalError); } - OptionDescription& OptionDescription::requires(const std::string& otherDottedName) { - return addConstraint(new RequiresOtherKeyConstraint(_dottedName, otherDottedName)); + return addConstraint(new NumericKeyConstraint(_dottedName, min, max)); +} + +OptionDescription& OptionDescription::incompatibleWith(const std::string& otherDottedName) { + return addConstraint(new MutuallyExclusiveKeyConstraint(_dottedName, otherDottedName)); +} + +OptionDescription& OptionDescription::requires(const std::string& otherDottedName) { + return addConstraint(new RequiresOtherKeyConstraint(_dottedName, otherDottedName)); +} + +OptionDescription& OptionDescription::format(const std::string& regexFormat, + const std::string& displayFormat) { + if (_type != String) { + StringBuilder sb; + sb << "Could not register option \"" << _dottedName << "\": " + << "only options registered as a string type can have a required format, " + << "but option has type: " << _type; + throw DBException(sb.str(), ErrorCodes::InternalError); } - OptionDescription& OptionDescription::format(const std::string& regexFormat, - const std::string& displayFormat) { - if (_type != String) { - StringBuilder sb; - sb << "Could not register option \"" << _dottedName << "\": " - << "only options registered as a string type can have a required format, " - << "but option has type: " << _type; - throw DBException(sb.str(), ErrorCodes::InternalError); - } - - return addConstraint(new StringFormatKeyConstraint(_dottedName, regexFormat, - displayFormat)); - } + return addConstraint(new StringFormatKeyConstraint(_dottedName, regexFormat, displayFormat)); +} -} // namespace optionenvironment -} // namespace mongo +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/option_description.h b/src/mongo/util/options_parser/option_description.h index 5bfe83c3a30..1dce8ea2980 100644 --- a/src/mongo/util/options_parser/option_description.h +++ b/src/mongo/util/options_parser/option_description.h @@ -37,200 +37,200 @@ namespace mongo { namespace optionenvironment { +/** + * An OptionType is an enum of all the types we support in the OptionsParser + * + * NOTE(sverch): The semantics of "Switch" options are completely identical to "Bool" options, + * except that on the command line they do not take a value. + */ +enum OptionType { + StringVector, // po::value< std::vector<std::string> > + StringMap, // po::value< std::vector<std::string> > (but in "key=value" format) + Bool, // po::value<bool> + Double, // po::value<double> + Int, // po::value<int> + Long, // po::value<long> + String, // po::value<std::string> + UnsignedLongLong, // po::value<unsigned long long> + Unsigned, // po::value<unsigned> + Switch // po::bool_switch +}; + +/** + * An OptionSources is an enum representing where an option can come from + */ +enum OptionSources { + SourceCommandLine = 1, + SourceINIConfig = 2, + SourceYAMLConfig = 4, + SourceAllConfig = SourceINIConfig | SourceYAMLConfig, + SourceAllLegacy = SourceINIConfig | SourceCommandLine, + SourceAll = SourceCommandLine | SourceINIConfig | SourceYAMLConfig +}; + +/** + * The OptionDescription class is a container for information about the options we are expecting + * either on the command line or in config files. These should be registered in an + * OptionSection instance and passed to an OptionsParser. + */ +class OptionDescription { +public: + OptionDescription(const std::string& dottedName, + const std::string& singleName, + const OptionType type, + const std::string& description); + + OptionDescription(const std::string& dottedName, + const std::string& singleName, + const OptionType type, + const std::string& description, + const std::vector<std::string>& deprecatedDottedNames); + + /* + * The following functions are part of the chaining interface for option registration. See + * comments below for what each of these attributes mean, and the OptionSection class for + * more details on the chaining interface. + */ + /** - * An OptionType is an enum of all the types we support in the OptionsParser + * Parsing Attributes. + * + * The functions below specify various attributes of our option that are relevant for + * parsing. + */ + + /* + * Make this option hidden so it does not appear in command line help + */ + OptionDescription& hidden(); + + /* + * Add a default value for this option if it is not specified + * + * throws DBException on errors, such as trying to set a default that does not have the same + * type as the option, or trying to set a default for a composing option. + */ + OptionDescription& setDefault(Value defaultValue); + + /* + * Add an implicit value for this option if it is specified with no argument + * + * throws DBException on errors, such as trying to set an implicit value that does not have + * the same type as the option, or trying to set an implicit value for a composing option. + */ + OptionDescription& setImplicit(Value implicitValue); + + /* + * Make this option composing so that the different sources add their values instead of + * overriding (eg. setParameter values in the config file and on the command line all get + * aggregated together) * - * NOTE(sverch): The semantics of "Switch" options are completely identical to "Bool" options, - * except that on the command line they do not take a value. + * throws DBException on errors, such as trying to make an option that is not a vector type + * composing, or or trying to set an implicit or default value for a composing option. + */ + OptionDescription& composing(); + + /* + * Specify the allowed sources for this option, such as CommandLine, JSONConfig, or + * INIConfig. The default is SourceAll which means the option can be present in any source + */ + OptionDescription& setSources(OptionSources sources); + + /* + * Specify that this is a positional option. "start" should be the first position the + * option can be found in, and "end" is the last position, inclusive. The positions start + * at index 1 (after the executable name). If "start" is greater than "end", then the + * option must be able to support multiple values. Specifying -1 for the "end" means that + * the option can repeat forever. Any "holes" in the positional ranges will result in an + * error during parsing. + * + * Examples: + * + * .positional(1,1) // Single positional argument at position 1 + * ... + * .positional(2,3) // More positional arguments at position 2 and 3 (multivalued option) + * ... + * .positional(4,-1) // Can repeat this positional option forever after position 4 + * + * + * (sverch) TODO: When we can support it (i.e. when we can get rid of boost) add a + * "positionalOnly" attribute that specifies that it is not also a command line flag. In + * boost program options, the only way to have a positional argument is to register a flag + * and mark it as also being positional. + */ + OptionDescription& positional(int start, int end); + + /** + * Validation Constraints. + * + * The functions below specify constraints that must be met in order for this option to be + * valid. These do not get checked during parsing, but will be added to the result + * Environment so that they will get checked when the Environment is validated. */ - enum OptionType { - StringVector, // po::value< std::vector<std::string> > - StringMap, // po::value< std::vector<std::string> > (but in "key=value" format) - Bool, // po::value<bool> - Double, // po::value<double> - Int, // po::value<int> - Long, // po::value<long> - String, // po::value<std::string> - UnsignedLongLong, // po::value<unsigned long long> - Unsigned, // po::value<unsigned> - Switch // po::bool_switch - }; /** - * An OptionSources is an enum representing where an option can come from + * Specifies the range allowed for this option. Only allowed for options with numeric type. */ - enum OptionSources { - SourceCommandLine = 1, - SourceINIConfig = 2, - SourceYAMLConfig = 4, - SourceAllConfig = SourceINIConfig | SourceYAMLConfig, - SourceAllLegacy = SourceINIConfig | SourceCommandLine, - SourceAll = SourceCommandLine | SourceINIConfig | SourceYAMLConfig - }; + OptionDescription& validRange(long min, long max); /** - * The OptionDescription class is a container for information about the options we are expecting - * either on the command line or in config files. These should be registered in an - * OptionSection instance and passed to an OptionsParser. + * Specifies that this option is incompatible with another option. The std::string provided must + * be the dottedName, which is the name used to access the option in the result Environment. + * + * TODO: Find a way to check that that option actually exists in our section somewhere. + */ + OptionDescription& incompatibleWith(const std::string& otherDottedName); + + /** + * Specifies that this option is requires another option to be specified. The string + * provided must be the dottedName, which is the name used to access the option in the + * result Environment. + */ + OptionDescription& requires(const std::string& otherDottedName); + + /** + * Specifies that this option is required to match the given format, specified as a regular + * expression. The displayFormat argument is what gets printed to the user in the case + * where this constraint is not satisfied. This is only allowed on std::string options. + */ + OptionDescription& format(const std::string& regexFormat, const std::string& displayFormat); + + /** + * Adds a constraint for this option. During parsing, this Constraint will be added to the + * result Environment, ensuring that it will get checked when the environment is validated. + * See the documentation on the Constraint and Environment classes for more details. + * + * WARNING: This function takes ownership of the Constraint pointer that is passed in. */ - class OptionDescription { - public: - OptionDescription(const std::string& dottedName, - const std::string& singleName, - const OptionType type, - const std::string& description); - - OptionDescription(const std::string& dottedName, - const std::string& singleName, - const OptionType type, - const std::string& description, - const std::vector<std::string>& deprecatedDottedNames); - - /* - * The following functions are part of the chaining interface for option registration. See - * comments below for what each of these attributes mean, and the OptionSection class for - * more details on the chaining interface. - */ - - /** - * Parsing Attributes. - * - * The functions below specify various attributes of our option that are relevant for - * parsing. - */ - - /* - * Make this option hidden so it does not appear in command line help - */ - OptionDescription& hidden(); - - /* - * Add a default value for this option if it is not specified - * - * throws DBException on errors, such as trying to set a default that does not have the same - * type as the option, or trying to set a default for a composing option. - */ - OptionDescription& setDefault(Value defaultValue); - - /* - * Add an implicit value for this option if it is specified with no argument - * - * throws DBException on errors, such as trying to set an implicit value that does not have - * the same type as the option, or trying to set an implicit value for a composing option. - */ - OptionDescription& setImplicit(Value implicitValue); - - /* - * Make this option composing so that the different sources add their values instead of - * overriding (eg. setParameter values in the config file and on the command line all get - * aggregated together) - * - * throws DBException on errors, such as trying to make an option that is not a vector type - * composing, or or trying to set an implicit or default value for a composing option. - */ - OptionDescription& composing(); - - /* - * Specify the allowed sources for this option, such as CommandLine, JSONConfig, or - * INIConfig. The default is SourceAll which means the option can be present in any source - */ - OptionDescription& setSources(OptionSources sources); - - /* - * Specify that this is a positional option. "start" should be the first position the - * option can be found in, and "end" is the last position, inclusive. The positions start - * at index 1 (after the executable name). If "start" is greater than "end", then the - * option must be able to support multiple values. Specifying -1 for the "end" means that - * the option can repeat forever. Any "holes" in the positional ranges will result in an - * error during parsing. - * - * Examples: - * - * .positional(1,1) // Single positional argument at position 1 - * ... - * .positional(2,3) // More positional arguments at position 2 and 3 (multivalued option) - * ... - * .positional(4,-1) // Can repeat this positional option forever after position 4 - * - * - * (sverch) TODO: When we can support it (i.e. when we can get rid of boost) add a - * "positionalOnly" attribute that specifies that it is not also a command line flag. In - * boost program options, the only way to have a positional argument is to register a flag - * and mark it as also being positional. - */ - OptionDescription& positional(int start, int end); - - /** - * Validation Constraints. - * - * The functions below specify constraints that must be met in order for this option to be - * valid. These do not get checked during parsing, but will be added to the result - * Environment so that they will get checked when the Environment is validated. - */ - - /** - * Specifies the range allowed for this option. Only allowed for options with numeric type. - */ - OptionDescription& validRange(long min, long max); - - /** - * Specifies that this option is incompatible with another option. The std::string provided must - * be the dottedName, which is the name used to access the option in the result Environment. - * - * TODO: Find a way to check that that option actually exists in our section somewhere. - */ - OptionDescription& incompatibleWith(const std::string& otherDottedName); - - /** - * Specifies that this option is requires another option to be specified. The string - * provided must be the dottedName, which is the name used to access the option in the - * result Environment. - */ - OptionDescription& requires(const std::string& otherDottedName); - - /** - * Specifies that this option is required to match the given format, specified as a regular - * expression. The displayFormat argument is what gets printed to the user in the case - * where this constraint is not satisfied. This is only allowed on std::string options. - */ - OptionDescription& format(const std::string& regexFormat, const std::string& displayFormat); - - /** - * Adds a constraint for this option. During parsing, this Constraint will be added to the - * result Environment, ensuring that it will get checked when the environment is validated. - * See the documentation on the Constraint and Environment classes for more details. - * - * WARNING: This function takes ownership of the Constraint pointer that is passed in. - */ - OptionDescription& addConstraint(Constraint* c); - - std::string _dottedName; // Used for JSON config and in Environment - std::string _singleName; // Used for boost command line and INI - OptionType _type; // Storage type of the argument value, or switch type (bool) - // (required by boost) - std::string _description; // Description of option printed in help output - bool _isVisible; // Visible in help output - Value _default; // Value if option is not specified - Value _implicit; // Value if option is specified with no argument - bool _isComposing; // Aggregate values from different sources instead of overriding - OptionSources _sources; // Places where an option can be specified (current sources are - // command line, json config, and ini config) - int _positionalStart; // The starting position if this is a positional option. -1 otherwise. - int _positionalEnd; // The ending position if this is a positional option. -1 if unlimited. - - // TODO(sverch): We have to use pointers to keep track of the Constrants because we rely on - // inheritance to make Constraints work. We have to use shared_ptrs because the - // OptionDescription is sometimes copied and because it is stored in a std::list in the - // OptionSection. We should think about a better solution for the ownership semantics of - // these classes. Note that the Environment (the storage for results of option parsing) has - // to know about the constraints for all the options, which is another factor to consider - // when thinking about ownership. - std::vector<boost::shared_ptr<Constraint> > _constraints; // Constraints that must be met - // for this option to be valid - - // Deprecated dotted names - aliases for '_dottedName'. - std::vector<std::string> _deprecatedDottedNames; - }; - -} // namespace optionenvironment -} // namespace mongo + OptionDescription& addConstraint(Constraint* c); + + std::string _dottedName; // Used for JSON config and in Environment + std::string _singleName; // Used for boost command line and INI + OptionType _type; // Storage type of the argument value, or switch type (bool) + // (required by boost) + std::string _description; // Description of option printed in help output + bool _isVisible; // Visible in help output + Value _default; // Value if option is not specified + Value _implicit; // Value if option is specified with no argument + bool _isComposing; // Aggregate values from different sources instead of overriding + OptionSources _sources; // Places where an option can be specified (current sources are + // command line, json config, and ini config) + int _positionalStart; // The starting position if this is a positional option. -1 otherwise. + int _positionalEnd; // The ending position if this is a positional option. -1 if unlimited. + + // TODO(sverch): We have to use pointers to keep track of the Constrants because we rely on + // inheritance to make Constraints work. We have to use shared_ptrs because the + // OptionDescription is sometimes copied and because it is stored in a std::list in the + // OptionSection. We should think about a better solution for the ownership semantics of + // these classes. Note that the Environment (the storage for results of option parsing) has + // to know about the constraints for all the options, which is another factor to consider + // when thinking about ownership. + std::vector<boost::shared_ptr<Constraint>> _constraints; // Constraints that must be met + // for this option to be valid + + // Deprecated dotted names - aliases for '_dottedName'. + std::vector<std::string> _deprecatedDottedNames; +}; + +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/option_section.cpp b/src/mongo/util/options_parser/option_section.cpp index 1e31ce44374..3a46c8a28d5 100644 --- a/src/mongo/util/options_parser/option_section.cpp +++ b/src/mongo/util/options_parser/option_section.cpp @@ -39,608 +39,579 @@ namespace mongo { namespace optionenvironment { - using boost::shared_ptr; +using boost::shared_ptr; - // Registration interface +// Registration interface - // TODO: Make sure the section we are adding does not have duplicate options - Status OptionSection::addSection(const OptionSection& subSection) { - std::list<OptionDescription>::const_iterator oditerator; - for (oditerator = subSection._options.begin(); - oditerator != subSection._options.end(); oditerator++) { - if (oditerator->_positionalStart != -1) { +// TODO: Make sure the section we are adding does not have duplicate options +Status OptionSection::addSection(const OptionSection& subSection) { + std::list<OptionDescription>::const_iterator oditerator; + for (oditerator = subSection._options.begin(); oditerator != subSection._options.end(); + oditerator++) { + if (oditerator->_positionalStart != -1) { + StringBuilder sb; + sb << "Attempted to add subsection with positional option: " << oditerator->_dottedName; + return Status(ErrorCodes::InternalError, sb.str()); + } + } + _subSections.push_back(subSection); + return Status::OK(); +} + +OptionDescription& OptionSection::addOptionChaining(const std::string& dottedName, + const std::string& singleName, + const OptionType type, + const std::string& description) { + std::vector<std::string> v; + return addOptionChaining(dottedName, singleName, type, description, v); +} + +OptionDescription& OptionSection::addOptionChaining(const std::string& dottedName, + const std::string& singleName, + const OptionType type, + const std::string& description, + const std::string& deprecatedDottedName) { + std::vector<std::string> v; + v.push_back(deprecatedDottedName); + return addOptionChaining(dottedName, singleName, type, description, v); +} + +OptionDescription& OptionSection::addOptionChaining( + const std::string& dottedName, + const std::string& singleName, + const OptionType type, + const std::string& description, + const std::vector<std::string>& deprecatedDottedNames) { + OptionDescription option(dottedName, singleName, type, description, deprecatedDottedNames); + + // Verify that single name, the dotted name and deprecated dotted names for this option + // conflicts with the names for any options we have already registered. + std::list<OptionDescription>::const_iterator oditerator; + for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { + if (option._dottedName == oditerator->_dottedName) { + StringBuilder sb; + sb << "Attempted to register option with duplicate dottedName: " << option._dottedName; + throw DBException(sb.str(), ErrorCodes::InternalError); + } + // Allow options with empty singleName since some options are not allowed on the command + // line + if (!option._singleName.empty() && option._singleName == oditerator->_singleName) { + StringBuilder sb; + sb << "Attempted to register option with duplicate singleName: " << option._singleName; + throw DBException(sb.str(), ErrorCodes::InternalError); + } + // Deprecated dotted names should not conflict with dotted names or deprecated dotted + // names of any other options. + if (std::count(option._deprecatedDottedNames.begin(), + option._deprecatedDottedNames.end(), + oditerator->_dottedName)) { + StringBuilder sb; + sb << "Attempted to register option with duplicate deprecated dotted name " + << "(with another option's dotted name): " << option._dottedName; + throw DBException(sb.str(), ErrorCodes::BadValue); + } + for (std::vector<std::string>::const_iterator i = + oditerator->_deprecatedDottedNames.begin(); + i != oditerator->_deprecatedDottedNames.end(); + ++i) { + if (std::count(option._deprecatedDottedNames.begin(), + option._deprecatedDottedNames.end(), + *i)) { StringBuilder sb; - sb << "Attempted to add subsection with positional option: " - << oditerator->_dottedName; - return Status(ErrorCodes::InternalError, sb.str()); + sb << "Attempted to register option with duplicate deprecated dotted name " << *i + << " (other option " << oditerator->_dottedName << ")"; + throw DBException(sb.str(), ErrorCodes::BadValue); } } - _subSections.push_back(subSection); - return Status::OK(); } - OptionDescription& OptionSection::addOptionChaining(const std::string& dottedName, - const std::string& singleName, - const OptionType type, - const std::string& description) { - std::vector<std::string> v; - return addOptionChaining(dottedName, singleName, type, description, v); + _options.push_back(option); + + return _options.back(); +} + +// Stuff for dealing with Boost + +namespace { + +/* + * Helper for option types that should be interpreted as a string by boost. We do this to + * take the responsibility away from boost for handling type conversions, since sometimes + * those conversions are inconsistent with our own. See SERVER-14110 For an example. + */ +template <typename Type> +Status typeToBoostStringType(std::auto_ptr<po::value_semantic>* boostType, + const Value defaultValue = Value(), + const Value implicitValue = Value()) { + std::auto_ptr<po::typed_value<std::string>> boostTypeBuilder(po::value<std::string>()); + + if (!implicitValue.isEmpty()) { + Type implicitValueType; + Status ret = implicitValue.get(&implicitValueType); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Error getting implicit value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + StringBuilder sb; + sb << implicitValueType; + boostTypeBuilder->implicit_value(sb.str()); } - OptionDescription& OptionSection::addOptionChaining(const std::string& dottedName, - const std::string& singleName, - const OptionType type, - const std::string& description, - const std::string& deprecatedDottedName) { - std::vector<std::string> v; - v.push_back(deprecatedDottedName); - return addOptionChaining(dottedName, singleName, type, description, v); + if (!defaultValue.isEmpty()) { + Type defaultValueType; + Status ret = defaultValue.get(&defaultValueType); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Error getting default value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + StringBuilder sb; + sb << defaultValueType; + boostTypeBuilder->default_value(sb.str()); } - OptionDescription& OptionSection::addOptionChaining(const std::string& dottedName, - const std::string& singleName, - const OptionType type, - const std::string& description, - const std::vector<std::string>& deprecatedDottedNames) { - OptionDescription option(dottedName, singleName, type, description, deprecatedDottedNames); - - // Verify that single name, the dotted name and deprecated dotted names for this option - // conflicts with the names for any options we have already registered. - std::list<OptionDescription>::const_iterator oditerator; - for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { - if (option._dottedName == oditerator->_dottedName) { - StringBuilder sb; - sb << "Attempted to register option with duplicate dottedName: " - << option._dottedName; - throw DBException(sb.str(), ErrorCodes::InternalError); - } - // Allow options with empty singleName since some options are not allowed on the command - // line - if (!option._singleName.empty() && option._singleName == oditerator->_singleName) { + *boostType = boostTypeBuilder; + + return Status::OK(); +} + +/** Helper function to convert the values of our OptionType enum into the classes that + * boost::program_option uses to pass around this information + */ +Status typeToBoostType(std::auto_ptr<po::value_semantic>* boostType, + OptionType type, + const Value defaultValue = Value(), + const Value implicitValue = Value(), + bool getSwitchAsBool = false) { + switch (type) { + case StringVector: { + *boostType = std::auto_ptr<po::value_semantic>(po::value<std::vector<std::string>>()); + + if (!implicitValue.isEmpty()) { StringBuilder sb; - sb << "Attempted to register option with duplicate singleName: " - << option._singleName; - throw DBException(sb.str(), ErrorCodes::InternalError); + sb << "Implicit value not supported for string vector"; + return Status(ErrorCodes::InternalError, sb.str()); } - // Deprecated dotted names should not conflict with dotted names or deprecated dotted - // names of any other options. - if (std::count(option._deprecatedDottedNames.begin(), - option._deprecatedDottedNames.end(), oditerator->_dottedName)) { + + if (!defaultValue.isEmpty()) { StringBuilder sb; - sb << "Attempted to register option with duplicate deprecated dotted name " - << "(with another option's dotted name): " - << option._dottedName; - throw DBException(sb.str(), ErrorCodes::BadValue); - } - for (std::vector<std::string>::const_iterator i = - oditerator->_deprecatedDottedNames.begin(); - i != oditerator->_deprecatedDottedNames.end(); ++i) { - if (std::count(option._deprecatedDottedNames.begin(), - option._deprecatedDottedNames.end(), *i)) { - StringBuilder sb; - sb << "Attempted to register option with duplicate deprecated dotted name " - << *i << " (other option " << oditerator->_dottedName << ")"; - throw DBException(sb.str(), ErrorCodes::BadValue); - } + sb << "Default value not supported for string vector"; + return Status(ErrorCodes::InternalError, sb.str()); } - } - - _options.push_back(option); - return _options.back(); - } + return Status::OK(); + } + case StringMap: { + // Boost doesn't support maps, so we just register a vector parameter and + // parse it as "key=value" strings + *boostType = std::auto_ptr<po::value_semantic>(po::value<std::vector<std::string>>()); - // Stuff for dealing with Boost + if (!implicitValue.isEmpty()) { + StringBuilder sb; + sb << "Implicit value not supported for string map"; + return Status(ErrorCodes::InternalError, sb.str()); + } - namespace { + if (!defaultValue.isEmpty()) { + StringBuilder sb; + sb << "Default value not supported for string map"; + return Status(ErrorCodes::InternalError, sb.str()); + } - /* - * Helper for option types that should be interpreted as a string by boost. We do this to - * take the responsibility away from boost for handling type conversions, since sometimes - * those conversions are inconsistent with our own. See SERVER-14110 For an example. - */ - template <typename Type> - Status typeToBoostStringType(std::auto_ptr<po::value_semantic>* boostType, - const Value defaultValue = Value(), - const Value implicitValue = Value()) { - std::auto_ptr<po::typed_value<std::string> > - boostTypeBuilder(po::value<std::string>()); + return Status::OK(); + } + case Switch: { + // In boost, switches default to false which makes it impossible to tell if + // a switch in a config file is not present or was explicitly set to false. + // + // Because of this, and because of the fact that we use the same set of + // options for the legacy key=value config file, we need a way to control + // whether we are telling boost that an option is a switch type or that an + // option is a bool type. + if (!getSwitchAsBool) { + *boostType = std::auto_ptr<po::value_semantic>(po::bool_switch()); + return Status::OK(); + } else { + // Switches should be true if they are present with no explicit value. + *boostType = + std::auto_ptr<po::typed_value<bool>>(po::value<bool>()->implicit_value(true)); + return Status::OK(); + } + } + case Bool: { + std::auto_ptr<po::typed_value<bool>> boostTypeBuilder(po::value<bool>()); if (!implicitValue.isEmpty()) { - Type implicitValueType; + bool implicitValueType; Status ret = implicitValue.get(&implicitValueType); - if(!ret.isOK()) { + if (!ret.isOK()) { StringBuilder sb; sb << "Error getting implicit value: " << ret.toString(); return Status(ErrorCodes::InternalError, sb.str()); } - StringBuilder sb; - sb << implicitValueType; - boostTypeBuilder->implicit_value(sb.str()); + boostTypeBuilder->implicit_value(implicitValueType); } if (!defaultValue.isEmpty()) { - Type defaultValueType; + bool defaultValueType; Status ret = defaultValue.get(&defaultValueType); - if(!ret.isOK()) { + if (!ret.isOK()) { StringBuilder sb; sb << "Error getting default value: " << ret.toString(); return Status(ErrorCodes::InternalError, sb.str()); } - StringBuilder sb; - sb << defaultValueType; - boostTypeBuilder->default_value(sb.str()); + boostTypeBuilder->default_value(defaultValueType); } *boostType = boostTypeBuilder; return Status::OK(); } - - /** Helper function to convert the values of our OptionType enum into the classes that - * boost::program_option uses to pass around this information - */ - Status typeToBoostType(std::auto_ptr<po::value_semantic>* boostType, - OptionType type, - const Value defaultValue = Value(), - const Value implicitValue = Value(), - bool getSwitchAsBool = false) { - switch (type) { - 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 StringMap: - { - // Boost doesn't support maps, so we just register a vector parameter and - // parse it as "key=value" strings - *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 map"; - return Status(ErrorCodes::InternalError, sb.str()); - } - - if (!defaultValue.isEmpty()) { - StringBuilder sb; - sb << "Default value not supported for string map"; - return Status(ErrorCodes::InternalError, sb.str()); - } - - return Status::OK(); - } - case Switch: - { - // In boost, switches default to false which makes it impossible to tell if - // a switch in a config file is not present or was explicitly set to false. - // - // Because of this, and because of the fact that we use the same set of - // options for the legacy key=value config file, we need a way to control - // whether we are telling boost that an option is a switch type or that an - // option is a bool type. - if (!getSwitchAsBool) { - *boostType = std::auto_ptr<po::value_semantic>(po::bool_switch()); - return Status::OK(); - } - else { - // Switches should be true if they are present with no explicit value. - *boostType = std::auto_ptr<po::typed_value<bool> >(po::value<bool>() - ->implicit_value(true)); - 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 String: - return typeToBoostStringType<std::string>(boostType, defaultValue, - implicitValue); - case Double: - return typeToBoostStringType<double>(boostType, defaultValue, implicitValue); - case Int: - return typeToBoostStringType<int>(boostType, defaultValue, implicitValue); - case Long: - return typeToBoostStringType<long>(boostType, defaultValue, implicitValue); - case UnsignedLongLong: - return typeToBoostStringType<unsigned long long>(boostType, defaultValue, - implicitValue); - case Unsigned: - return typeToBoostStringType<unsigned>(boostType, defaultValue, implicitValue); - default: - { - StringBuilder sb; - sb << "Unrecognized option type: " << type; - return Status(ErrorCodes::InternalError, sb.str()); - } - } - } - } // namespace - - Status OptionSection::getBoostOptions(po::options_description* boostOptions, - bool visibleOnly, - bool includeDefaults, - OptionSources sources, - bool getEmptySections) const { - - std::list<OptionDescription>::const_iterator oditerator; - for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { - // Only include this option if it matches the sources we specified and the option is - // either visible or we are requesting hidden options - if ((!visibleOnly || (oditerator->_isVisible)) && - (oditerator->_sources & sources)) { - std::auto_ptr<po::value_semantic> boostType; - Status ret = typeToBoostType(&boostType, - oditerator->_type, - includeDefaults ? oditerator->_default : Value(), - oditerator->_implicit, - !(sources & SourceCommandLine)); - if (!ret.isOK()) { - StringBuilder sb; - sb << "Error getting boost type for option \"" - << oditerator->_dottedName << "\": " << ret.toString(); - return Status(ErrorCodes::InternalError, sb.str()); - } - - if (oditerator->_singleName.empty()) { - StringBuilder sb; - sb << "Single name is empty for option \"" - << oditerator->_dottedName << "\", but trying to use it on the command line " - << "or INI config file. Only options that are exclusive to the YAML config " - << "file can have an empty single name"; - return Status(ErrorCodes::InternalError, sb.str()); - } - - boostOptions->add_options()(oditerator->_singleName.c_str(), - boostType.release(), - oditerator->_description.c_str()); - } + case String: + return typeToBoostStringType<std::string>(boostType, defaultValue, implicitValue); + case Double: + return typeToBoostStringType<double>(boostType, defaultValue, implicitValue); + case Int: + return typeToBoostStringType<int>(boostType, defaultValue, implicitValue); + case Long: + return typeToBoostStringType<long>(boostType, defaultValue, implicitValue); + case UnsignedLongLong: + return typeToBoostStringType<unsigned long long>( + boostType, defaultValue, implicitValue); + case Unsigned: + return typeToBoostStringType<unsigned>(boostType, defaultValue, implicitValue); + default: { + StringBuilder sb; + sb << "Unrecognized option type: " << type; + return Status(ErrorCodes::InternalError, sb.str()); } - - std::list<OptionSection>::const_iterator ositerator; - for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { - po::options_description subGroup = ositerator->_name.empty() - ? po::options_description() - : po::options_description(ositerator->_name.c_str()); - - // Do not add empty sections to our option_description unless we specifically requested. - int numOptions; - Status ret = ositerator->countOptions(&numOptions, visibleOnly, sources); + } +} +} // namespace + +Status OptionSection::getBoostOptions(po::options_description* boostOptions, + bool visibleOnly, + bool includeDefaults, + OptionSources sources, + bool getEmptySections) const { + std::list<OptionDescription>::const_iterator oditerator; + for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { + // Only include this option if it matches the sources we specified and the option is + // either visible or we are requesting hidden options + if ((!visibleOnly || (oditerator->_isVisible)) && (oditerator->_sources & sources)) { + std::auto_ptr<po::value_semantic> boostType; + Status ret = typeToBoostType(&boostType, + oditerator->_type, + includeDefaults ? oditerator->_default : Value(), + oditerator->_implicit, + !(sources & SourceCommandLine)); if (!ret.isOK()) { - return ret; - } - if (numOptions == 0 && getEmptySections == false) { - continue; + StringBuilder sb; + sb << "Error getting boost type for option \"" << oditerator->_dottedName + << "\": " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); } - ret = ositerator->getBoostOptions(&subGroup, visibleOnly, includeDefaults, - sources, getEmptySections); - if (!ret.isOK()) { - return ret; + if (oditerator->_singleName.empty()) { + StringBuilder sb; + sb << "Single name is empty for option \"" << oditerator->_dottedName + << "\", but trying to use it on the command line " + << "or INI config file. Only options that are exclusive to the YAML config " + << "file can have an empty single name"; + return Status(ErrorCodes::InternalError, sb.str()); } - boostOptions->add(subGroup); - } - - return Status::OK(); - } - /* - * The way we specify positional options in our interface differs from the way boost does it, so - * we have to convert them here. - * - * For example, to specify positionals such that you can run "./exec [pos1] [pos2] [pos2]": - * - * Our interface: - * - * options.addOptionChaining("pos2", "pos2", moe::StringVector, "Pos2") - * .hidden() <- doesn't show up in help - * .sources(moe::SourceCommandLine) <- only allowed on command line - * .positional(2, <- start position - * 3); <- end position - * options.addOptionChaining("pos1", "pos1", moe::String, "Pos1") - * .hidden() <- doesn't show up in help - * .sources(moe::SourceCommandLine) <- only allowed on command line - * .positional(1, <- start position - * 1); <- end position - * // Note that order doesn't matter - * - * Boost's interface: - * - * boostHiddenOptions->add_options()("pos1", po::value<std::string>(), "Pos1") - * ("pos2", po::value<std::string>(), "Pos2") - * - * boostPositionalOptions->add("pos1", 1); <- count of option (number of times it appears) - * boostPositionalOptions->add("pos2", 2); <- count of option (number of times it appears) - * // Note that order does matter - * - * Because of this, we have to perform the conversion in this function. The tasks performed by - * this function are: - * - * 1. Making sure the ranges are valid as a whole (no overlap or holes) - * 2. Convert to the boost options and add them in the correct order - */ - Status OptionSection::getBoostPositionalOptions( - po::positional_options_description* boostPositionalOptions) const { - - std::list<OptionDescription> positionalOptions; - - std::list<OptionDescription>::const_iterator oditerator; - for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { - // Check if this is a positional option, and extract it if it is - if (oditerator->_positionalStart != -1) { - positionalOptions.push_back(*oditerator); - } + boostOptions->add_options()(oditerator->_singleName.c_str(), + boostType.release(), + oditerator->_description.c_str()); } + } - int nextPosition = 1; - bool foundAtPosition = false; - while (!positionalOptions.empty()) { - foundAtPosition = false; - std::list<OptionDescription>::iterator poditerator; - for (poditerator = positionalOptions.begin(); poditerator != positionalOptions.end();) { - - if (poditerator->_positionalStart < nextPosition) { - StringBuilder sb; - sb << "Found option with overlapping positional range: " - << " Expected next option at position: " << nextPosition - << ", but \"" << poditerator->_dottedName << "\" starts at position: " - << poditerator->_positionalStart; - return Status(ErrorCodes::InternalError, sb.str()); - } - - if (poditerator->_positionalStart == nextPosition) { - foundAtPosition = true; - - int count; - if (poditerator->_positionalEnd == -1) { - count = -1; - if (positionalOptions.size() != 1) { - StringBuilder sb; - sb << "Found positional option with infinite count, but still have " - << "more positional options registered"; - return Status(ErrorCodes::InternalError, sb.str()); - } - } - else { - count = (poditerator->_positionalEnd + 1) - poditerator->_positionalStart; - } + std::list<OptionSection>::const_iterator ositerator; + for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { + po::options_description subGroup = ositerator->_name.empty() + ? po::options_description() + : po::options_description(ositerator->_name.c_str()); - boostPositionalOptions->add(poditerator->_dottedName.c_str(), count); - nextPosition += count; - std::list<OptionDescription>::iterator old_poditerator = poditerator; - poditerator++; - positionalOptions.erase(old_poditerator); - } - else { - poditerator++; - } - } - if (!foundAtPosition) { - StringBuilder sb; - sb << "Did not find option at position: " << nextPosition; - return Status(ErrorCodes::InternalError, sb.str()); - } + // Do not add empty sections to our option_description unless we specifically requested. + int numOptions; + Status ret = ositerator->countOptions(&numOptions, visibleOnly, sources); + if (!ret.isOK()) { + return ret; + } + if (numOptions == 0 && getEmptySections == false) { + continue; } - // XXX: Right now only the top level section can have positional options - - return Status::OK(); + ret = ositerator->getBoostOptions( + &subGroup, visibleOnly, includeDefaults, sources, getEmptySections); + if (!ret.isOK()) { + return ret; + } + boostOptions->add(subGroup); } - // Get options for iterating - // TODO: should I make this an iterator? + return Status::OK(); +} - Status OptionSection::getAllOptions(std::vector<OptionDescription>* options) const { - - std::list<OptionDescription>::const_iterator oditerator; - for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { +/* + * The way we specify positional options in our interface differs from the way boost does it, so + * we have to convert them here. + * + * For example, to specify positionals such that you can run "./exec [pos1] [pos2] [pos2]": + * + * Our interface: + * + * options.addOptionChaining("pos2", "pos2", moe::StringVector, "Pos2") + * .hidden() <- doesn't show up in help + * .sources(moe::SourceCommandLine) <- only allowed on command line + * .positional(2, <- start position + * 3); <- end position + * options.addOptionChaining("pos1", "pos1", moe::String, "Pos1") + * .hidden() <- doesn't show up in help + * .sources(moe::SourceCommandLine) <- only allowed on command line + * .positional(1, <- start position + * 1); <- end position + * // Note that order doesn't matter + * + * Boost's interface: + * + * boostHiddenOptions->add_options()("pos1", po::value<std::string>(), "Pos1") + * ("pos2", po::value<std::string>(), "Pos2") + * + * boostPositionalOptions->add("pos1", 1); <- count of option (number of times it appears) + * boostPositionalOptions->add("pos2", 2); <- count of option (number of times it appears) + * // Note that order does matter + * + * Because of this, we have to perform the conversion in this function. The tasks performed by + * this function are: + * + * 1. Making sure the ranges are valid as a whole (no overlap or holes) + * 2. Convert to the boost options and add them in the correct order + */ +Status OptionSection::getBoostPositionalOptions( + po::positional_options_description* boostPositionalOptions) const { + std::list<OptionDescription> positionalOptions; + + std::list<OptionDescription>::const_iterator oditerator; + for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { + // Check if this is a positional option, and extract it if it is + if (oditerator->_positionalStart != -1) { + positionalOptions.push_back(*oditerator); + } + } - // We need to check here that we didn't register an option with an empty single name - // that is allowed on the command line or in an old style config, since we don't have - // this information available all at once when the option is registered - if (oditerator->_singleName.empty() && - oditerator->_sources & SourceAllLegacy) { + int nextPosition = 1; + bool foundAtPosition = false; + while (!positionalOptions.empty()) { + foundAtPosition = false; + std::list<OptionDescription>::iterator poditerator; + for (poditerator = positionalOptions.begin(); poditerator != positionalOptions.end();) { + if (poditerator->_positionalStart < nextPosition) { StringBuilder sb; - sb << "Found option allowed on the command line with an empty singleName: " - << oditerator->_dottedName; + sb << "Found option with overlapping positional range: " + << " Expected next option at position: " << nextPosition << ", but \"" + << poditerator->_dottedName + << "\" starts at position: " << poditerator->_positionalStart; return Status(ErrorCodes::InternalError, sb.str()); } - options->push_back(*oditerator); - } - - std::list<OptionSection>::const_iterator ositerator; - for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { - ositerator->getAllOptions(options); - } - - return Status::OK(); - } + if (poditerator->_positionalStart == nextPosition) { + foundAtPosition = true; - Status OptionSection::getDefaults(std::map<Key, Value>* values) const { + int count; + if (poditerator->_positionalEnd == -1) { + count = -1; + if (positionalOptions.size() != 1) { + StringBuilder sb; + sb << "Found positional option with infinite count, but still have " + << "more positional options registered"; + return Status(ErrorCodes::InternalError, sb.str()); + } + } else { + count = (poditerator->_positionalEnd + 1) - poditerator->_positionalStart; + } - std::list<OptionDescription>::const_iterator oditerator; - for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { - if (!oditerator->_default.isEmpty()) { - (*values)[oditerator->_dottedName] = oditerator->_default; + boostPositionalOptions->add(poditerator->_dottedName.c_str(), count); + nextPosition += count; + std::list<OptionDescription>::iterator old_poditerator = poditerator; + poditerator++; + positionalOptions.erase(old_poditerator); + } else { + poditerator++; } } - - std::list<OptionSection>::const_iterator ositerator; - for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { - ositerator->getDefaults(values); + if (!foundAtPosition) { + StringBuilder sb; + sb << "Did not find option at position: " << nextPosition; + return Status(ErrorCodes::InternalError, sb.str()); } - - return Status::OK(); } - Status OptionSection::countOptions(int* numOptions, - bool visibleOnly, - OptionSources sources) const { + // XXX: Right now only the top level section can have positional options - *numOptions = 0; + return Status::OK(); +} - std::list<OptionDescription>::const_iterator oditerator; - for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { - // Only count this option if it matches the sources we specified and the option is - // either visible or we are requesting hidden options - if ((!visibleOnly || (oditerator->_isVisible)) && - (oditerator->_sources & sources)) { - (*numOptions)++; - } - } +// Get options for iterating +// TODO: should I make this an iterator? - std::list<OptionSection>::const_iterator ositerator; - for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { - int numSubOptions = 0; - ositerator->countOptions(&numSubOptions, visibleOnly, sources); - *numOptions += numSubOptions; +Status OptionSection::getAllOptions(std::vector<OptionDescription>* options) const { + std::list<OptionDescription>::const_iterator oditerator; + for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { + // We need to check here that we didn't register an option with an empty single name + // that is allowed on the command line or in an old style config, since we don't have + // this information available all at once when the option is registered + if (oditerator->_singleName.empty() && oditerator->_sources & SourceAllLegacy) { + StringBuilder sb; + sb << "Found option allowed on the command line with an empty singleName: " + << oditerator->_dottedName; + return Status(ErrorCodes::InternalError, sb.str()); } - return Status::OK(); + options->push_back(*oditerator); } - Status OptionSection::getConstraints( - std::vector<boost::shared_ptr<Constraint > >* constraints) const { + std::list<OptionSection>::const_iterator ositerator; + for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { + ositerator->getAllOptions(options); + } - std::list<OptionDescription>::const_iterator oditerator; - for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { - std::vector<boost::shared_ptr<Constraint> >::const_iterator citerator; - for (citerator = oditerator->_constraints.begin(); - citerator != oditerator->_constraints.end(); citerator++) { - constraints->push_back(*citerator); - } - } + return Status::OK(); +} - std::list<OptionSection>::const_iterator ositerator; - for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { - ositerator->getConstraints(constraints); +Status OptionSection::getDefaults(std::map<Key, Value>* values) const { + std::list<OptionDescription>::const_iterator oditerator; + for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { + if (!oditerator->_default.isEmpty()) { + (*values)[oditerator->_dottedName] = oditerator->_default; } + } - return Status::OK(); + std::list<OptionSection>::const_iterator ositerator; + for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { + ositerator->getDefaults(values); } - std::string OptionSection::positionalHelpString(const std::string& execName) const { + return Status::OK(); +} - po::positional_options_description boostPositionalOptions; - Status ret = getBoostPositionalOptions(&boostPositionalOptions); - if (!ret.isOK()) { - StringBuilder sb; - sb << "Error constructing help string: " << ret.toString(); - return sb.str(); +Status OptionSection::countOptions(int* numOptions, bool visibleOnly, OptionSources sources) const { + *numOptions = 0; + + std::list<OptionDescription>::const_iterator oditerator; + for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { + // Only count this option if it matches the sources we specified and the option is + // either visible or we are requesting hidden options + if ((!visibleOnly || (oditerator->_isVisible)) && (oditerator->_sources & sources)) { + (*numOptions)++; } + } - StringBuilder posHelpStringBuilder; - posHelpStringBuilder << execName; + std::list<OptionSection>::const_iterator ositerator; + for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { + int numSubOptions = 0; + ositerator->countOptions(&numSubOptions, visibleOnly, sources); + *numOptions += numSubOptions; + } - // If we can have unlimited positional options, this returns - // std::numeric_limits<unsigned>::max(). Check here for that case and record what name to - // look for. - unsigned int numPositional = boostPositionalOptions.max_total_count(); - std::string trailingPositionName; - if (numPositional == std::numeric_limits<unsigned>::max()) { - trailingPositionName = boostPositionalOptions.name_for_position(numPositional - 1); + return Status::OK(); +} + +Status OptionSection::getConstraints( + std::vector<boost::shared_ptr<Constraint>>* constraints) const { + std::list<OptionDescription>::const_iterator oditerator; + for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { + std::vector<boost::shared_ptr<Constraint>>::const_iterator citerator; + for (citerator = oditerator->_constraints.begin(); + citerator != oditerator->_constraints.end(); + citerator++) { + constraints->push_back(*citerator); } + } - unsigned int position; - std::string positionName; - for (position = 0; position < numPositional; position++) { - positionName = boostPositionalOptions.name_for_position(position); - if (!trailingPositionName.empty() && trailingPositionName == positionName) { - // If we have a trailing position, we break when we see it the first time. - posHelpStringBuilder << " [" << trailingPositionName << " ... ]"; - break; - } - posHelpStringBuilder << " [" << positionName << "]"; - } + std::list<OptionSection>::const_iterator ositerator; + for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { + ositerator->getConstraints(constraints); + } - return posHelpStringBuilder.str(); + return Status::OK(); +} + +std::string OptionSection::positionalHelpString(const std::string& execName) const { + po::positional_options_description boostPositionalOptions; + Status ret = getBoostPositionalOptions(&boostPositionalOptions); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Error constructing help string: " << ret.toString(); + return sb.str(); } - std::string OptionSection::helpString() const { + StringBuilder posHelpStringBuilder; + posHelpStringBuilder << execName; - po::options_description boostOptions = _name.empty() - ? po::options_description() - : po::options_description(_name.c_str()); - Status ret = getBoostOptions(&boostOptions, - true, /* visibleOnly */ - true, /* includeDefaults */ - SourceAllLegacy, - false); /* getEmptySections */ - if (!ret.isOK()) { - StringBuilder sb; - sb << "Error constructing help string: " << ret.toString(); - return sb.str(); - } + // If we can have unlimited positional options, this returns + // std::numeric_limits<unsigned>::max(). Check here for that case and record what name to + // look for. + unsigned int numPositional = boostPositionalOptions.max_total_count(); + std::string trailingPositionName; + if (numPositional == std::numeric_limits<unsigned>::max()) { + trailingPositionName = boostPositionalOptions.name_for_position(numPositional - 1); + } - // Can't use a StringBuilder here because boost::program_options only has functions that - // output to std::ostream - std::ostringstream os; - os << boostOptions; - return os.str(); + unsigned int position; + std::string positionName; + for (position = 0; position < numPositional; position++) { + positionName = boostPositionalOptions.name_for_position(position); + if (!trailingPositionName.empty() && trailingPositionName == positionName) { + // If we have a trailing position, we break when we see it the first time. + posHelpStringBuilder << " [" << trailingPositionName << " ... ]"; + break; + } + posHelpStringBuilder << " [" << positionName << "]"; } - /* Debugging */ - void OptionSection::dump() const { + return posHelpStringBuilder.str(); +} + +std::string OptionSection::helpString() const { + po::options_description boostOptions = + _name.empty() ? po::options_description() : po::options_description(_name.c_str()); + Status ret = getBoostOptions(&boostOptions, + true, /* visibleOnly */ + true, /* includeDefaults */ + SourceAllLegacy, + false); /* getEmptySections */ + if (!ret.isOK()) { + StringBuilder sb; + sb << "Error constructing help string: " << ret.toString(); + return sb.str(); + } - std::list<OptionDescription>::const_iterator oditerator; - for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { - std::cout << " _dottedName: " << oditerator->_dottedName - << " _singleName: " << oditerator->_singleName - << " _type: " << oditerator->_type - << " _description: " << oditerator->_description - << " _isVisible: " << oditerator->_isVisible << std::endl; - } + // Can't use a StringBuilder here because boost::program_options only has functions that + // output to std::ostream + std::ostringstream os; + os << boostOptions; + return os.str(); +} + +/* Debugging */ +void OptionSection::dump() const { + std::list<OptionDescription>::const_iterator oditerator; + for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { + std::cout << " _dottedName: " << oditerator->_dottedName + << " _singleName: " << oditerator->_singleName << " _type: " << oditerator->_type + << " _description: " << oditerator->_description + << " _isVisible: " << oditerator->_isVisible << std::endl; + } - std::list<OptionSection>::const_iterator ositerator; - for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { - std::cout << "Section Name: " << ositerator->_name << std::endl; - ositerator->dump(); - } + std::list<OptionSection>::const_iterator ositerator; + for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { + std::cout << "Section Name: " << ositerator->_name << std::endl; + ositerator->dump(); } +} -} // namespace optionenvironment -} // namespace mongo +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/option_section.h b/src/mongo/util/options_parser/option_section.h index 572e2b60e0d..1377b4e9a98 100644 --- a/src/mongo/util/options_parser/option_section.h +++ b/src/mongo/util/options_parser/option_section.h @@ -37,137 +37,137 @@ namespace mongo { namespace optionenvironment { - namespace po = boost::program_options; +namespace po = boost::program_options; + +/** + * A container for OptionDescription instances as well as other OptionSection instances. + * Provides a description of all options that are supported to be passed in to an + * OptionsParser. Has utility functions to support the various formats needed by the parsing + * process + * + * The sections and section names only matter in the help string. For sections in a JSON + * config, look at the dots in the dottedName of the relevant OptionDescription + * + * Usage: + * + * namespace moe = mongo::optionenvironment; + * + * moe::OptionsParser parser; + * moe::Environment environment; + * moe::OptionSection options; + * moe::OptionSection subSection("Section Name"); + * + * // Register our allowed option flags with our OptionSection + * options.addOptionChaining("help", "help", moe::Switch, "Display Help"); + * + * // Register our positional options with our OptionSection + * options.addOptionChaining("command", "command", moe::String, "Command").positional(1, 1); + * + * // Add a subsection + * subSection.addOptionChaining("port", "port", moe::Int, "Port"); + * options.addSection(subSection); + * + * // Run the parser + * Status ret = parser.run(options, argc, argv, envp, &environment); + * if (!ret.isOK()) { + * cerr << options.helpString() << std::endl; + * exit(EXIT_FAILURE); + * } + */ + +class OptionSection { +public: + OptionSection(const std::string& name) : _name(name) {} + OptionSection() {} + + // Construction interface /** - * A container for OptionDescription instances as well as other OptionSection instances. - * Provides a description of all options that are supported to be passed in to an - * OptionsParser. Has utility functions to support the various formats needed by the parsing - * process - * - * The sections and section names only matter in the help string. For sections in a JSON - * config, look at the dots in the dottedName of the relevant OptionDescription + * Add a sub section to this section. Used mainly to keep track of section headers for when + * we need generate the help std::string for the command line + */ + Status addSection(const OptionSection& subSection); + + /** + * Add an option to this section, and returns a reference to an OptionDescription to allow + * for chaining. * - * Usage: + * Examples: * - * namespace moe = mongo::optionenvironment; + * options.addOptionChaining("option", "option", moe::String, "Chaining Registration") + * .hidden().setDefault(moe::Value("default")) + * .setImplicit(moe::Value("implicit")); * - * moe::OptionsParser parser; - * moe::Environment environment; - * moe::OptionSection options; - * moe::OptionSection subSection("Section Name"); + * This creates a hidden option that has default and implicit values. * - * // Register our allowed option flags with our OptionSection - * options.addOptionChaining("help", "help", moe::Switch, "Display Help"); + * options.addOptionChaining("name", "name", moe::String, "Composing Option") + * .composing().sources(SourceAllConfig); * - * // Register our positional options with our OptionSection - * options.addOptionChaining("command", "command", moe::String, "Command").positional(1, 1); + * This creates an option that is composing and can be specified only in config files. * - * // Add a subsection - * subSection.addOptionChaining("port", "port", moe::Int, "Port"); - * options.addSection(subSection); + * See the OptionDescription class for details on the supported attributes. * - * // Run the parser - * Status ret = parser.run(options, argc, argv, envp, &environment); - * if (!ret.isOK()) { - * cerr << options.helpString() << std::endl; - * exit(EXIT_FAILURE); - * } + * throws DBException on errors, such as attempting to register an option with the same name + * as another option. These represent programming errors that should not happen during + * normal operation. + */ + OptionDescription& addOptionChaining(const std::string& dottedName, + const std::string& singleName, + const OptionType type, + const std::string& description); + + OptionDescription& addOptionChaining(const std::string& dottedName, + const std::string& singleName, + const OptionType type, + const std::string& description, + const std::string& deprecatedDottedName); + + OptionDescription& addOptionChaining(const std::string& dottedName, + const std::string& singleName, + const OptionType type, + const std::string& description, + const std::vector<std::string>& deprecatedDottedNames); + + // These functions are used by the OptionsParser to make calls into boost::program_options + Status getBoostOptions(po::options_description* boostOptions, + bool visibleOnly = false, + bool includeDefaults = false, + OptionSources = SourceAll, + bool getEmptySections = true) const; + Status getBoostPositionalOptions( + po::positional_options_description* boostPositionalOptions) const; + + // This is needed so that the parser can iterate over all registered options to get the + // correct names when populating the Environment, as well as check that a parameter that was + // found has been registered and has the correct type + Status getAllOptions(std::vector<OptionDescription>* options) const; + + // Count the number of options in this section and all subsections + Status countOptions(int* numOptions, bool visibleOnly, OptionSources sources) 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; + + /** + * Populates the given vector with all the constraints for all options in this section and + * sub sections. + */ + Status getConstraints(std::vector<boost::shared_ptr<Constraint>>* constraints) const; + + std::string positionalHelpString(const std::string& execName) const; + std::string helpString() const; + + // Debugging + void dump() const; + +private: + std::string _name; + std::list<OptionSection> _subSections; + std::list<OptionDescription> _options; +}; - class OptionSection { - public: - OptionSection(const std::string& name) : _name(name) { } - OptionSection() { } - - // Construction interface - - /** - * Add a sub section to this section. Used mainly to keep track of section headers for when - * we need generate the help std::string for the command line - */ - Status addSection(const OptionSection& subSection); - - /** - * Add an option to this section, and returns a reference to an OptionDescription to allow - * for chaining. - * - * Examples: - * - * options.addOptionChaining("option", "option", moe::String, "Chaining Registration") - * .hidden().setDefault(moe::Value("default")) - * .setImplicit(moe::Value("implicit")); - * - * This creates a hidden option that has default and implicit values. - * - * options.addOptionChaining("name", "name", moe::String, "Composing Option") - * .composing().sources(SourceAllConfig); - * - * This creates an option that is composing and can be specified only in config files. - * - * See the OptionDescription class for details on the supported attributes. - * - * throws DBException on errors, such as attempting to register an option with the same name - * as another option. These represent programming errors that should not happen during - * normal operation. - */ - OptionDescription& addOptionChaining(const std::string& dottedName, - const std::string& singleName, - const OptionType type, - const std::string& description); - - OptionDescription& addOptionChaining(const std::string& dottedName, - const std::string& singleName, - const OptionType type, - const std::string& description, - const std::string& deprecatedDottedName); - - OptionDescription& addOptionChaining(const std::string& dottedName, - const std::string& singleName, - const OptionType type, - const std::string& description, - const std::vector<std::string>& deprecatedDottedNames); - - // These functions are used by the OptionsParser to make calls into boost::program_options - Status getBoostOptions(po::options_description* boostOptions, - bool visibleOnly = false, - bool includeDefaults = false, - OptionSources = SourceAll, - bool getEmptySections = true) const; - Status getBoostPositionalOptions( - po::positional_options_description* boostPositionalOptions) const; - - // This is needed so that the parser can iterate over all registered options to get the - // correct names when populating the Environment, as well as check that a parameter that was - // found has been registered and has the correct type - Status getAllOptions(std::vector<OptionDescription>* options) const; - - // Count the number of options in this section and all subsections - Status countOptions(int* numOptions, bool visibleOnly, OptionSources sources) 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; - - /** - * Populates the given vector with all the constraints for all options in this section and - * sub sections. - */ - Status getConstraints(std::vector<boost::shared_ptr<Constraint > >* constraints) const; - - std::string positionalHelpString(const std::string& execName) const; - std::string helpString() const; - - // Debugging - void dump() const; - - private: - std::string _name; - std::list<OptionSection> _subSections; - std::list<OptionDescription> _options; - }; - -} // namespace optionenvironment -} // namespace mongo +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/options_parser.cpp b/src/mongo/util/options_parser/options_parser.cpp index e4d4817f546..10ad484a3b3 100644 --- a/src/mongo/util/options_parser/options_parser.cpp +++ b/src/mongo/util/options_parser/options_parser.cpp @@ -52,997 +52,954 @@ namespace mongo { namespace optionenvironment { - using namespace std; - using boost::shared_ptr; - - namespace po = boost::program_options; - - namespace { - - // The following section contains utility functions that convert between the various objects - // we need to deal with while parsing command line options. - // - // These conversions are different depending on the data source because our current - // implementation uses boost::program_options for the command line and INI files and the - // yaml-cpp YAML parser for YAML config files. Our destination storage in both cases is an - // Environment which stores Value objects. - // - // 1. YAML Config Files - // The YAML parser parses a YAML config file into a YAML::Node. Therefore, we need: - // a. A function to convert a YAML::Node to a Value (YAMLNodeToValue) - // b. A function to iterate a YAML::Node, convert the leaf Nodes to Values, and add - // them to our Environment (addYAMLNodesToEnvironment) - // - // 2. INI Config Files and command line - // The boost::program_options parsers store their output in a - // boost::program_options::variables_map. Therefore, we need: - // a. A function to convert a boost::any to a Value (boostAnyToValue) - // b. A function to iterate a variables_map, convert the boost::any elements to - // Values, and add them to our Environment (addBoostVariablesToEnvironment) - - // Attempts to convert a string to a value of the given type. - Status stringToValue(const std::string& stringVal, - const OptionType& type, - const Key& key, Value* value) { - - Status ret = Status::OK(); - switch (type) { - double doubleVal; - int intVal; - long longVal; - unsigned long long unsignedLongLongVal; - unsigned unsignedVal; - case Switch: - if (stringVal == "true") { - *value = Value(true); - return Status::OK(); - } - else if (stringVal == "false") { - *value = Value(false); - return Status::OK(); - } - else { - StringBuilder sb; - sb << "Expected boolean switch but found string: " << stringVal - << " for option: " << key; - return Status(ErrorCodes::BadValue, sb.str()); - } - case Bool: - if (stringVal == "true") { - *value = Value(true); - return Status::OK(); - } - else if (stringVal == "false") { - *value = Value(false); - return Status::OK(); - } - else { - StringBuilder sb; - sb << "Expected boolean but found string: " << stringVal - << " for option: " << key; - return Status(ErrorCodes::BadValue, sb.str()); - } - case Double: - ret = parseNumberFromString(stringVal, &doubleVal); - if (!ret.isOK()) { - StringBuilder sb; - sb << "Error parsing option \"" << key - << "\" as double in: " << ret.reason(); - return Status(ErrorCodes::BadValue, sb.str()); - } - *value = Value(doubleVal); - return Status::OK(); - case Int: - ret = parseNumberFromString(stringVal, &intVal); - if (!ret.isOK()) { - StringBuilder sb; - sb << "Error parsing option \"" << key - << "\" as int: " << ret.reason(); - return Status(ErrorCodes::BadValue, sb.str()); - } - *value = Value(intVal); - return Status::OK(); - case Long: - ret = parseNumberFromString(stringVal, &longVal); - if (!ret.isOK()) { - StringBuilder sb; - sb << "Error parsing option \"" << key - << "\" as long: " << ret.reason(); - return Status(ErrorCodes::BadValue, sb.str()); - } - *value = Value(longVal); - return Status::OK(); - case String: - *value = Value(stringVal); - return Status::OK(); - case UnsignedLongLong: - ret = parseNumberFromString(stringVal, &unsignedLongLongVal); - if (!ret.isOK()) { - StringBuilder sb; - sb << "Error parsing option \"" << key - << "\" as unsigned long long: " << ret.reason(); - return Status(ErrorCodes::BadValue, sb.str()); - } - *value = Value(unsignedLongLongVal); - return Status::OK(); - case Unsigned: - ret = parseNumberFromString(stringVal, &unsignedVal); - if (!ret.isOK()) { - StringBuilder sb; - sb << "Error parsing option \"" << key - << "\" as unsigned int: " << ret.reason(); - return Status(ErrorCodes::BadValue, sb.str()); - } - *value = Value(unsignedVal); - return Status::OK(); - default: /* XXX: should not get here */ - return Status(ErrorCodes::InternalError, "Unrecognized option type"); - } - } +using namespace std; +using boost::shared_ptr; - // Convert a boost::any to a Value. See comments at the beginning of this section. - Status boostAnyToValue(const boost::any& anyValue, - const OptionType& type, - const Key& key, Value* value) { - try { - if (anyValue.type() == typeid(StringVector_t)) { - *value = Value(boost::any_cast<StringVector_t>(anyValue)); - } - else if (anyValue.type() == typeid(bool)) { - *value = Value(boost::any_cast<bool>(anyValue)); - } - else if (anyValue.type() == typeid(std::string)) { - return stringToValue(boost::any_cast<std::string>(anyValue), type, key, value); - } - // We should not be telling boost about numerical type information. Instead, for - // any numerical type we tell boost to read a string value and parse it manually, - // since boost's parsing is not consistent with ours. See SERVER-14110. - else if (anyValue.type() == typeid(double) || anyValue.type() == typeid(int) || - anyValue.type() == typeid(long) || anyValue.type() == typeid(unsigned) || - anyValue.type() == typeid(unsigned long long)) { - StringBuilder sb; - sb << "Found int type: " << anyValue.type().name() << - " in any to Value conversion, which is not supported"; - return Status(ErrorCodes::InternalError, sb.str()); - } - else { - StringBuilder sb; - sb << "Unrecognized type: " << anyValue.type().name() << - " in any to Value conversion"; - return Status(ErrorCodes::InternalError, sb.str()); - } +namespace po = boost::program_options; + +namespace { + +// The following section contains utility functions that convert between the various objects +// we need to deal with while parsing command line options. +// +// These conversions are different depending on the data source because our current +// implementation uses boost::program_options for the command line and INI files and the +// yaml-cpp YAML parser for YAML config files. Our destination storage in both cases is an +// Environment which stores Value objects. +// +// 1. YAML Config Files +// The YAML parser parses a YAML config file into a YAML::Node. Therefore, we need: +// a. A function to convert a YAML::Node to a Value (YAMLNodeToValue) +// b. A function to iterate a YAML::Node, convert the leaf Nodes to Values, and add +// them to our Environment (addYAMLNodesToEnvironment) +// +// 2. INI Config Files and command line +// The boost::program_options parsers store their output in a +// boost::program_options::variables_map. Therefore, we need: +// a. A function to convert a boost::any to a Value (boostAnyToValue) +// b. A function to iterate a variables_map, convert the boost::any elements to +// Values, and add them to our Environment (addBoostVariablesToEnvironment) + +// Attempts to convert a string to a value of the given type. +Status stringToValue(const std::string& stringVal, + const OptionType& type, + const Key& key, + Value* value) { + Status ret = Status::OK(); + switch (type) { + double doubleVal; + int intVal; + long longVal; + unsigned long long unsignedLongLongVal; + unsigned unsignedVal; + case Switch: + if (stringVal == "true") { + *value = Value(true); + return Status::OK(); + } else if (stringVal == "false") { + *value = Value(false); + return Status::OK(); + } else { + StringBuilder sb; + sb << "Expected boolean switch but found string: " << stringVal + << " for option: " << key; + return Status(ErrorCodes::BadValue, sb.str()); } - catch(const boost::bad_any_cast& e) { + case Bool: + if (stringVal == "true") { + *value = Value(true); + return Status::OK(); + } else if (stringVal == "false") { + *value = Value(false); + return Status::OK(); + } else { StringBuilder sb; - // We already checked the type, so this is just a sanity check - sb << "boost::any_cast threw exception: " << e.what(); - return Status(ErrorCodes::InternalError, sb.str()); + sb << "Expected boolean but found string: " << stringVal << " for option: " << key; + return Status(ErrorCodes::BadValue, sb.str()); } + case Double: + ret = parseNumberFromString(stringVal, &doubleVal); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Error parsing option \"" << key << "\" as double in: " << ret.reason(); + return Status(ErrorCodes::BadValue, sb.str()); + } + *value = Value(doubleVal); + return Status::OK(); + case Int: + ret = parseNumberFromString(stringVal, &intVal); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Error parsing option \"" << key << "\" as int: " << ret.reason(); + return Status(ErrorCodes::BadValue, sb.str()); + } + *value = Value(intVal); + return Status::OK(); + case Long: + ret = parseNumberFromString(stringVal, &longVal); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Error parsing option \"" << key << "\" as long: " << ret.reason(); + return Status(ErrorCodes::BadValue, sb.str()); + } + *value = Value(longVal); return Status::OK(); + case String: + *value = Value(stringVal); + return Status::OK(); + case UnsignedLongLong: + ret = parseNumberFromString(stringVal, &unsignedLongLongVal); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Error parsing option \"" << key + << "\" as unsigned long long: " << ret.reason(); + return Status(ErrorCodes::BadValue, sb.str()); + } + *value = Value(unsignedLongLongVal); + return Status::OK(); + case Unsigned: + ret = parseNumberFromString(stringVal, &unsignedVal); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Error parsing option \"" << key << "\" as unsigned int: " << ret.reason(); + return Status(ErrorCodes::BadValue, sb.str()); + } + *value = Value(unsignedVal); + return Status::OK(); + default: /* XXX: should not get here */ + return Status(ErrorCodes::InternalError, "Unrecognized option type"); + } +} + +// Convert a boost::any to a Value. See comments at the beginning of this section. +Status boostAnyToValue(const boost::any& anyValue, + const OptionType& type, + const Key& key, + Value* value) { + try { + if (anyValue.type() == typeid(StringVector_t)) { + *value = Value(boost::any_cast<StringVector_t>(anyValue)); + } else if (anyValue.type() == typeid(bool)) { + *value = Value(boost::any_cast<bool>(anyValue)); + } else if (anyValue.type() == typeid(std::string)) { + return stringToValue(boost::any_cast<std::string>(anyValue), type, key, value); + } + // We should not be telling boost about numerical type information. Instead, for + // any numerical type we tell boost to read a string value and parse it manually, + // since boost's parsing is not consistent with ours. See SERVER-14110. + else if (anyValue.type() == typeid(double) || anyValue.type() == typeid(int) || + anyValue.type() == typeid(long) || anyValue.type() == typeid(unsigned) || + anyValue.type() == typeid(unsigned long long)) { + StringBuilder sb; + sb << "Found int type: " << anyValue.type().name() + << " in any to Value conversion, which is not supported"; + return Status(ErrorCodes::InternalError, sb.str()); + } else { + StringBuilder sb; + sb << "Unrecognized type: " << anyValue.type().name() << " in any to Value conversion"; + return Status(ErrorCodes::InternalError, sb.str()); + } + } catch (const boost::bad_any_cast& e) { + StringBuilder sb; + // We already checked the type, so this is just a sanity check + sb << "boost::any_cast threw exception: " << e.what(); + return Status(ErrorCodes::InternalError, sb.str()); + } + return Status::OK(); +} + +// Returns true if the option for the given key is a StringMap option, and false otherwise +bool OptionIsStringMap(const std::vector<OptionDescription>& options_vector, const Key& key) { + for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); + iterator++) { + if (key == iterator->_dottedName && (iterator->_sources & SourceYAMLConfig)) { + if (iterator->_type == StringMap) { + return true; + } } + } - // Returns true if the option for the given key is a StringMap option, and false otherwise - bool OptionIsStringMap(const std::vector<OptionDescription>& options_vector, - const Key& key) { + return false; +} + +// Convert a YAML::Node to a Value. See comments at the beginning of this section. +// 'canonicalKey' holds the dotted name that should be used in the result Environment. +// This ensures that both canonical and deprecated dotted names in the configuration +// are mapped to the canonical name. +Status YAMLNodeToValue(const YAML::Node& YAMLNode, + const std::vector<OptionDescription>& options_vector, + const Key& key, + Key* canonicalKey, + Value* value) { + bool isRegistered = false; + + // The logic below should ensure that we don't use this uninitialized, but we need to + // initialize it here to avoid a compiler warning. Initializing it to a "Bool" since + // that's the most restricted type we have and is most likely to result in an early + // failure if we have a logic error. + OptionType type = Bool; + + // The config file had a ":" as the first non whitespace character on a line + if (key.empty()) { + StringBuilder sb; + sb << "Found empty key in YAML config file"; + return Status(ErrorCodes::BadValue, sb.str()); + } - for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin(); - iterator != options_vector.end(); iterator++) { - if (key == iterator->_dottedName && (iterator->_sources & SourceYAMLConfig)) { - if (iterator->_type == StringMap) { - return true; - } - } + // Get expected type + for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); + iterator++) { + if (!(iterator->_sources & SourceYAMLConfig)) { + continue; + } + + bool isDeprecated = std::count(iterator->_deprecatedDottedNames.begin(), + iterator->_deprecatedDottedNames.end(), + key) > 0; + if (key == iterator->_dottedName || isDeprecated) { + isRegistered = true; + type = iterator->_type; + *canonicalKey = iterator->_dottedName; + if (isDeprecated) { + warning() << "Option: " << key << " is deprecated. Please use " + << iterator->_dottedName << " instead."; } - - return false; } + } + + if (!isRegistered) { + StringBuilder sb; + sb << "Unrecognized option: " << key; + return Status(ErrorCodes::BadValue, sb.str()); + } - // Convert a YAML::Node to a Value. See comments at the beginning of this section. - // 'canonicalKey' holds the dotted name that should be used in the result Environment. - // This ensures that both canonical and deprecated dotted names in the configuration - // are mapped to the canonical name. - Status YAMLNodeToValue(const YAML::Node& YAMLNode, - const std::vector<OptionDescription>& options_vector, - const Key& key, - Key* canonicalKey, - Value* value) { - - bool isRegistered = false; - - // The logic below should ensure that we don't use this uninitialized, but we need to - // initialize it here to avoid a compiler warning. Initializing it to a "Bool" since - // that's the most restricted type we have and is most likely to result in an early - // failure if we have a logic error. - OptionType type = Bool; - - // The config file had a ":" as the first non whitespace character on a line - if (key.empty()) { + // Handle multi keys + if (type == StringVector) { + if (!YAMLNode.IsSequence()) { + StringBuilder sb; + sb << "Option: " << key + << " is of type StringVector, but value in YAML config is not a list type"; + return Status(ErrorCodes::BadValue, sb.str()); + } + StringVector_t stringVector; + for (YAML::const_iterator it = YAMLNode.begin(); it != YAMLNode.end(); ++it) { + if (it->IsSequence()) { StringBuilder sb; - sb << "Found empty key in YAML config file"; + sb << "Option: " << key << " has nested lists, which is not allowed"; return Status(ErrorCodes::BadValue, sb.str()); } + stringVector.push_back(it->Scalar()); + } + *value = Value(stringVector); + return Status::OK(); + } - // Get expected type - for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin(); - iterator != options_vector.end(); iterator++) { - if (!(iterator->_sources & SourceYAMLConfig)) { - continue; - } - - bool isDeprecated = std::count(iterator->_deprecatedDottedNames.begin(), - iterator->_deprecatedDottedNames.end(), key) > 0; - if (key == iterator->_dottedName || isDeprecated) { - isRegistered = true; - type = iterator->_type; - *canonicalKey = iterator->_dottedName; - if (isDeprecated) { - warning() << "Option: " << key << " is deprecated. Please use " - << iterator->_dottedName << " instead."; - } - } + // Handle a sub map as a value + if (type == StringMap) { + if (!YAMLNode.IsMap()) { + StringBuilder sb; + sb << "Option: " << key + << " is of type StringMap, but value in YAML config is not a map type"; + return Status(ErrorCodes::BadValue, sb.str()); + } + StringMap_t stringMap; + for (YAML::const_iterator it = YAMLNode.begin(); it != YAMLNode.end(); ++it) { + if (it->second.IsSequence() || it->second.IsMap()) { + StringBuilder sb; + sb << "Option: " << key + << " has a map with non scalar values, which is not allowed"; + return Status(ErrorCodes::BadValue, sb.str()); } + if (stringMap.count(it->first.Scalar()) > 0) { + StringBuilder sb; + sb << "String Map Option: " << key + << " has duplicate keys in YAML Config: " << it->first.Scalar(); + return Status(ErrorCodes::BadValue, sb.str()); + } + stringMap[it->first.Scalar()] = it->second.Scalar(); + } + *value = Value(stringMap); + return Status::OK(); + } - if (!isRegistered) { + // Our YAML parser reads everything as a string, so we need to parse it ourselves. + std::string stringVal = YAMLNode.Scalar(); + return stringToValue(stringVal, type, key, value); +} + +// Add all the values in the given variables_map to our environment. See comments at the +// beginning of this section. +Status addBoostVariablesToEnvironment(const po::variables_map& vm, + const OptionSection& options, + Environment* environment) { + 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++) { + // Trim off the short option from our name so we can look it up correctly in our map + std::string long_name; + std::string::size_type commaOffset = iterator->_singleName.find(','); + if (commaOffset != string::npos) { + if (commaOffset != iterator->_singleName.size() - 2) { StringBuilder sb; - sb << "Unrecognized option: " << key; + sb << "Unexpected comma in option name: \"" << iterator->_singleName << "\"" + << ": option name must be in the format \"option,o\" or \"option\", " + << "where \"option\" is the long name and \"o\" is the optional one " + << "character short alias"; return Status(ErrorCodes::BadValue, sb.str()); } + long_name = iterator->_singleName.substr(0, commaOffset); + } else { + long_name = iterator->_singleName; + } - // Handle multi keys - if (type == StringVector) { - if (!YAMLNode.IsSequence()) { - StringBuilder sb; - sb << "Option: " << key - << " is of type StringVector, but value in YAML config is not a list type"; - return Status(ErrorCodes::BadValue, sb.str()); - } - StringVector_t stringVector; - for (YAML::const_iterator it = YAMLNode.begin(); it != YAMLNode.end(); ++it) { - if (it->IsSequence()) { - StringBuilder sb; - sb << "Option: " << key - << " has nested lists, which is not allowed"; - return Status(ErrorCodes::BadValue, sb.str()); - } - stringVector.push_back(it->Scalar()); - } - *value = Value(stringVector); - return Status::OK(); + if (vm.count(long_name)) { + Value optionValue; + Status ret = + boostAnyToValue(vm[long_name].value(), iterator->_type, long_name, &optionValue); + if (!ret.isOK()) { + return ret; } - // Handle a sub map as a value - if (type == StringMap) { - if (!YAMLNode.IsMap()) { - StringBuilder sb; - sb << "Option: " << key - << " is of type StringMap, but value in YAML config is not a map type"; - return Status(ErrorCodes::BadValue, sb.str()); + // If this is really a StringMap, try to split on "key=value" for each element + // in our StringVector + if (iterator->_type == StringMap) { + StringVector_t keyValueVector; + ret = optionValue.get(&keyValueVector); + if (!ret.isOK()) { + return ret; } - StringMap_t stringMap; - for (YAML::const_iterator it = YAMLNode.begin(); it != YAMLNode.end(); ++it) { - if (it->second.IsSequence() || it->second.IsMap()) { + StringMap_t mapValue; + for (StringVector_t::iterator keyValueVectorIt = keyValueVector.begin(); + keyValueVectorIt != keyValueVector.end(); + ++keyValueVectorIt) { + std::string key; + std::string value; + if (!mongoutils::str::splitOn(*keyValueVectorIt, '=', key, value)) { StringBuilder sb; - sb << "Option: " << key - << " has a map with non scalar values, which is not allowed"; + sb << "Illegal option assignment: \"" << *keyValueVectorIt << "\""; return Status(ErrorCodes::BadValue, sb.str()); } - if (stringMap.count(it->first.Scalar()) > 0) { + // Make sure we aren't setting an option to two different values + if (mapValue.count(key) > 0 && mapValue[key] != value) { StringBuilder sb; - sb << "String Map Option: " << key - << " has duplicate keys in YAML Config: " << it->first.Scalar(); + sb << "Key Value Option: " << iterator->_dottedName + << " has a duplicate key from the same source: " << key; return Status(ErrorCodes::BadValue, sb.str()); } - stringMap[it->first.Scalar()] = it->second.Scalar(); + mapValue[key] = value; } - *value = Value(stringMap); - return Status::OK(); + optionValue = Value(mapValue); } - // Our YAML parser reads everything as a string, so we need to parse it ourselves. - std::string stringVal = YAMLNode.Scalar(); - return stringToValue(stringVal, type, key, value); + environment->set(iterator->_dottedName, optionValue); } + } + return Status::OK(); +} + +// Add all the values in the given YAML Node to our environment. See comments at the +// beginning of this section. +Status addYAMLNodesToEnvironment(const YAML::Node& root, + const OptionSection& options, + const std::string parentPath, + Environment* environment) { + std::vector<OptionDescription> options_vector; + Status ret = options.getAllOptions(&options_vector); + if (!ret.isOK()) { + return ret; + } - // Add all the values in the given variables_map to our environment. See comments at the - // beginning of this section. - Status addBoostVariablesToEnvironment(const po::variables_map& vm, - const OptionSection& options, - Environment* environment) { - - 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++) { - - // Trim off the short option from our name so we can look it up correctly in our map - std::string long_name; - std::string::size_type commaOffset = iterator->_singleName.find(','); - if (commaOffset != string::npos) { - if (commaOffset != iterator->_singleName.size() - 2) { - StringBuilder sb; - sb << "Unexpected comma in option name: \"" << iterator->_singleName << "\"" - << ": option name must be in the format \"option,o\" or \"option\", " - << "where \"option\" is the long name and \"o\" is the optional one " - << "character short alias"; - return Status(ErrorCodes::BadValue, sb.str()); - } - long_name = iterator->_singleName.substr(0, commaOffset); - } else { - long_name = iterator->_singleName; - } + // Don't return an error on empty config files + if (root.IsNull()) { + return Status::OK(); + } - if (vm.count(long_name)) { - Value optionValue; - Status ret = boostAnyToValue(vm[long_name].value(), iterator->_type, long_name, - &optionValue); - if (!ret.isOK()) { - return ret; - } + if (!root.IsMap() && parentPath.empty()) { + StringBuilder sb; + sb << "No map found at top level of YAML config"; + return Status(ErrorCodes::BadValue, sb.str()); + } - // If this is really a StringMap, try to split on "key=value" for each element - // in our StringVector - if (iterator->_type == StringMap) { - StringVector_t keyValueVector; - ret = optionValue.get(&keyValueVector); - if (!ret.isOK()) { - return ret; - } - StringMap_t mapValue; - for (StringVector_t::iterator keyValueVectorIt = keyValueVector.begin(); - keyValueVectorIt != keyValueVector.end(); ++keyValueVectorIt) { - std::string key; - std::string value; - if (!mongoutils::str::splitOn(*keyValueVectorIt, '=', key, value)) { - StringBuilder sb; - sb << "Illegal option assignment: \"" << *keyValueVectorIt << "\""; - return Status(ErrorCodes::BadValue, sb.str()); - } - // Make sure we aren't setting an option to two different values - if (mapValue.count(key) > 0 && mapValue[key] != value) { - StringBuilder sb; - sb << "Key Value Option: " << iterator->_dottedName - << " has a duplicate key from the same source: " << key; - return Status(ErrorCodes::BadValue, sb.str()); - } - mapValue[key] = value; - } - optionValue = Value(mapValue); - } + for (YAML::const_iterator it = root.begin(); it != root.end(); ++it) { + std::string fieldName = it->first.Scalar(); + YAML::Node YAMLNode = it->second; + + std::string dottedName; + if (parentPath.empty()) { + // We are at the top level, so the full specifier is just the current field name + dottedName = fieldName; + } else { + // If our field name is "value", assume this contains the value for the parent + if (fieldName == "value") { + dottedName = parentPath; + } - environment->set(iterator->_dottedName, optionValue); - } + // If this is not a special field name, and we are in a sub object, append our + // current fieldName to the selector for the sub object we are traversing + else { + dottedName = parentPath + '.' + fieldName; } - return Status::OK(); } - // Add all the values in the given YAML Node to our environment. See comments at the - // beginning of this section. - Status addYAMLNodesToEnvironment(const YAML::Node& root, - const OptionSection& options, - const std::string parentPath, - Environment* environment) { - - std::vector<OptionDescription> options_vector; - Status ret = options.getAllOptions(&options_vector); + if (YAMLNode.IsMap() && !OptionIsStringMap(options_vector, dottedName)) { + Status ret = addYAMLNodesToEnvironment(YAMLNode, options, dottedName, environment); if (!ret.isOK()) { return ret; } - - // Don't return an error on empty config files - if (root.IsNull()) { - return Status::OK(); + } else { + Key canonicalKey; + Value optionValue; + Status ret = + YAMLNodeToValue(YAMLNode, options_vector, dottedName, &canonicalKey, &optionValue); + if (!ret.isOK()) { + return ret; } - if (!root.IsMap() && parentPath.empty()) { + Value dummyVal; + if (environment->get(canonicalKey, &dummyVal).isOK()) { StringBuilder sb; - sb << "No map found at top level of YAML config"; + sb << "Error parsing YAML config: duplicate key: " << dottedName + << "(canonical key: " << canonicalKey << ")"; return Status(ErrorCodes::BadValue, sb.str()); } - for (YAML::const_iterator it = root.begin(); it != root.end(); ++it) { - std::string fieldName = it->first.Scalar(); - YAML::Node YAMLNode = it->second; - - std::string dottedName; - if (parentPath.empty()) { - - // We are at the top level, so the full specifier is just the current field name - dottedName = fieldName; + // Only add the value if it is not empty. YAMLNodeToValue will set the + // optionValue to an empty Value if we should not set it in the Environment. + if (!optionValue.isEmpty()) { + ret = environment->set(canonicalKey, optionValue); + if (!ret.isOK()) { + return ret; } - else { + } + } + } - // If our field name is "value", assume this contains the value for the parent - if (fieldName == "value") { - dottedName = parentPath; - } + 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; + } - // If this is not a special field name, and we are in a sub object, append our - // current fieldName to the selector for the sub object we are traversing - else { - dottedName = parentPath + '.' + fieldName; - } - } + for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); + iterator++) { + if (!iterator->_isComposing) { + continue; + } - if (YAMLNode.IsMap() && !OptionIsStringMap(options_vector, dottedName)) { - Status ret = addYAMLNodesToEnvironment(YAMLNode, options, dottedName, - environment); - if (!ret.isOK()) { - return ret; - } + if (iterator->_type == StringVector) { + StringVector_t sourceValue; + ret = source.get(iterator->_dottedName, &sourceValue); + 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()) { + StringVector_t destValue; + ret = dest->get(iterator->_dottedName, &destValue); + if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) { + StringBuilder sb; + sb << "Error getting composable vector value from dest: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); } - else { - Key canonicalKey; - Value optionValue; - Status ret = YAMLNodeToValue(YAMLNode, options_vector, dottedName, - &canonicalKey, &optionValue); - if (!ret.isOK()) { - return ret; - } - Value dummyVal; - if (environment->get(canonicalKey, &dummyVal).isOK()) { - StringBuilder sb; - sb << "Error parsing YAML config: duplicate key: " << dottedName - << "(canonical key: " << canonicalKey << ")"; - return Status(ErrorCodes::BadValue, sb.str()); - } + // Append sourceValue on the end of destValue + destValue.insert(destValue.end(), sourceValue.begin(), sourceValue.end()); - // Only add the value if it is not empty. YAMLNodeToValue will set the - // optionValue to an empty Value if we should not set it in the Environment. - if (!optionValue.isEmpty()) { - ret = environment->set(canonicalKey, optionValue); - if (!ret.isOK()) { - return ret; - } - } + // Set the resulting value in our output environment + ret = dest->set(Key(iterator->_dottedName), Value(destValue)); + if (!ret.isOK()) { + return ret; } } - - 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; + } else if (iterator->_type == StringMap) { + StringMap_t sourceValue; + ret = source.get(iterator->_dottedName, &sourceValue); + if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) { + StringBuilder sb; + sb << "Error getting composable map value from source: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); } - - for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin(); - iterator != options_vector.end(); iterator++) { - - if (!iterator->_isComposing) { - continue; + // Only do something if our source environment has something to add + else if (ret.isOK()) { + StringMap_t destValue; + ret = dest->get(iterator->_dottedName, &destValue); + if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) { + StringBuilder sb; + sb << "Error getting composable map value from dest: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); } - if (iterator->_type == StringVector) { - StringVector_t sourceValue; - ret = source.get(iterator->_dottedName, &sourceValue); - 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()) { - StringVector_t destValue; - ret = dest->get(iterator->_dottedName, &destValue); - 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 sourceValue on the end of destValue - destValue.insert(destValue.end(), - sourceValue.begin(), - sourceValue.end()); - - // Set the resulting value in our output environment - ret = dest->set(Key(iterator->_dottedName), Value(destValue)); - if (!ret.isOK()) { - return ret; - } - } + // Iterate sourceValue and add elements to destValue + for (StringMap_t::iterator sourceValueIt = sourceValue.begin(); + sourceValueIt != sourceValue.end(); + sourceValueIt++) { + destValue[sourceValueIt->first] = sourceValueIt->second; } - else if (iterator->_type == StringMap) { - StringMap_t sourceValue; - ret = source.get(iterator->_dottedName, &sourceValue); - if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) { - StringBuilder sb; - sb << "Error getting composable map 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()) { - StringMap_t destValue; - ret = dest->get(iterator->_dottedName, &destValue); - if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) { - StringBuilder sb; - sb << "Error getting composable map value from dest: " - << ret.toString(); - return Status(ErrorCodes::InternalError, sb.str()); - } - - // Iterate sourceValue and add elements to destValue - for (StringMap_t::iterator sourceValueIt = sourceValue.begin(); - sourceValueIt != sourceValue.end(); sourceValueIt++) { - destValue[sourceValueIt->first] = sourceValueIt->second; - } - - // Set the resulting value in our output environment - ret = dest->set(Key(iterator->_dottedName), Value(destValue)); - if (!ret.isOK()) { - return ret; - } - } - } else { - StringBuilder sb; - sb << "Found composable option that is not of StringVector or " - << "StringMap Type: " << iterator->_dottedName; - return Status(ErrorCodes::InternalError, sb.str()); + + // Set the resulting value in our output environment + ret = dest->set(Key(iterator->_dottedName), Value(destValue)); + if (!ret.isOK()) { + return ret; } } - - return Status::OK(); + } else { + StringBuilder sb; + sb << "Found composable option that is not of StringVector or " + << "StringMap Type: " << iterator->_dottedName; + return Status(ErrorCodes::InternalError, sb.str()); } + } - /** - * For all options that have constraints, add those constraints to our environment so that - * they run when the environment gets validated. - */ - Status addConstraints(const OptionSection& options, Environment* dest) { - std::vector<boost::shared_ptr<Constraint> > constraints_vector; + return Status::OK(); +} - Status ret = options.getConstraints(&constraints_vector); - if (!ret.isOK()) { - return ret; - } +/** +* For all options that have constraints, add those constraints to our environment so that +* they run when the environment gets validated. +*/ +Status addConstraints(const OptionSection& options, Environment* dest) { + std::vector<boost::shared_ptr<Constraint>> constraints_vector; - std::vector<boost::shared_ptr<Constraint> >::const_iterator citerator; - for (citerator = constraints_vector.begin(); - citerator != constraints_vector.end(); citerator++) { - dest->addConstraint(citerator->get()); - } + Status ret = options.getConstraints(&constraints_vector); + if (!ret.isOK()) { + return ret; + } - return Status::OK(); - } + std::vector<boost::shared_ptr<Constraint>>::const_iterator citerator; + for (citerator = constraints_vector.begin(); citerator != constraints_vector.end(); + citerator++) { + dest->addConstraint(citerator->get()); + } - /** - * Remove any options of type "Switch" that are set to false. This is needed because boost - * defaults switches to false, and we need to be able to tell the difference between - * whether an option is set explicitly to false in config files or not present at all. - */ - Status removeFalseSwitches(const OptionSection& options, Environment* environment) { - std::vector<OptionDescription> options_vector; - Status ret = options.getAllOptions(&options_vector); - if (!ret.isOK()) { - return ret; - } + return Status::OK(); +} - for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin(); - iterator != options_vector.end(); iterator++) { +/** + * Remove any options of type "Switch" that are set to false. This is needed because boost + * defaults switches to false, and we need to be able to tell the difference between + * whether an option is set explicitly to false in config files or not present at all. + */ +Status removeFalseSwitches(const OptionSection& options, Environment* environment) { + std::vector<OptionDescription> options_vector; + Status ret = options.getAllOptions(&options_vector); + if (!ret.isOK()) { + return ret; + } - if (iterator->_type == Switch) { - bool switchValue; - Status ret = environment->get(iterator->_dottedName, &switchValue); - if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) { - StringBuilder sb; - sb << "Error getting switch value for option: " << iterator->_dottedName - << " from source: " << ret.toString(); - return Status(ErrorCodes::InternalError, sb.str()); - } - else if (ret.isOK() && switchValue == false) { - Status ret = environment->remove(iterator->_dottedName); - if (!ret.isOK()) { - StringBuilder sb; - sb << "Error removing false flag: " << iterator->_dottedName << ": " - << ret.toString(); - return Status(ErrorCodes::InternalError, sb.str()); - } - } + for (std::vector<OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); + iterator++) { + if (iterator->_type == Switch) { + bool switchValue; + Status ret = environment->get(iterator->_dottedName, &switchValue); + if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) { + StringBuilder sb; + sb << "Error getting switch value for option: " << iterator->_dottedName + << " from source: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } else if (ret.isOK() && switchValue == false) { + Status ret = environment->remove(iterator->_dottedName); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Error removing false flag: " << iterator->_dottedName << ": " + << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); } } - - return Status::OK(); } + } + + return Status::OK(); +} + +} // namespace - } // namespace +/** + * This function delegates the command line parsing to boost program_options. + * + * 1. Extract the boost readable option descriptions and positional option descriptions from the + * OptionSection + * 2. Passes them to the boost command line parser + * 3. Copy everything from the variables map returned by boost into the Environment + */ +Status OptionsParser::parseCommandLine(const OptionSection& options, + const std::vector<std::string>& argv, + Environment* environment) { + po::options_description boostOptions; + po::positional_options_description boostPositionalOptions; + po::variables_map vm; + + // Need to convert to an argc and a vector of char* since that is what + // boost::program_options expects as input to its command line parser + int argc = 0; + std::vector<const char*> argv_buffer; + for (std::vector<std::string>::const_iterator iterator = argv.begin(); iterator != argv.end(); + iterator++) { + argv_buffer.push_back(iterator->c_str()); + argc++; + } /** - * This function delegates the command line parsing to boost program_options. + * Style options for boost command line parser * - * 1. Extract the boost readable option descriptions and positional option descriptions from the - * OptionSection - * 2. Passes them to the boost command line parser - * 3. Copy everything from the variables map returned by boost into the Environment + * unix_style is an alias for a group of basic style options. We are deviating from that + * base style in the following ways: + * + * 1. Don't allow guessing - '--dbpat' != '--dbpath' + * 2. Don't allow sticky - '-hf' != '-h -f' + * 3. Allow long disguises - '--dbpath' == '-dbpath' + * + * In some executables, we are using multiple 'v' options to set the verbosity (e.g. '-vvv') + * To make this work, we need to allow long disguises and disallow guessing. */ - Status OptionsParser::parseCommandLine(const OptionSection& options, - const std::vector<std::string>& argv, - Environment* environment) { - po::options_description boostOptions; - po::positional_options_description boostPositionalOptions; - po::variables_map vm; - - // Need to convert to an argc and a vector of char* since that is what - // boost::program_options expects as input to its command line parser - int argc = 0; - std::vector<const char*> argv_buffer; - for (std::vector<std::string>::const_iterator iterator = argv.begin(); - iterator != argv.end(); iterator++) { - argv_buffer.push_back(iterator->c_str()); - argc++; - } + int style = (((po::command_line_style::unix_style ^ po::command_line_style::allow_guessing) | + po::command_line_style::allow_long_disguise) ^ + po::command_line_style::allow_sticky); - /** - * Style options for boost command line parser - * - * unix_style is an alias for a group of basic style options. We are deviating from that - * base style in the following ways: - * - * 1. Don't allow guessing - '--dbpat' != '--dbpath' - * 2. Don't allow sticky - '-hf' != '-h -f' - * 3. Allow long disguises - '--dbpath' == '-dbpath' - * - * In some executables, we are using multiple 'v' options to set the verbosity (e.g. '-vvv') - * To make this work, we need to allow long disguises and disallow guessing. - */ - int style = (((po::command_line_style::unix_style ^ - po::command_line_style::allow_guessing) | - po::command_line_style::allow_long_disguise) ^ - po::command_line_style::allow_sticky); - - Status ret = options.getBoostOptions(&boostOptions, false, false, SourceCommandLine); - if (!ret.isOK()) { - return ret; - } - - ret = options.getBoostPositionalOptions(&boostPositionalOptions); - if (!ret.isOK()) { - return ret; - } + Status ret = options.getBoostOptions(&boostOptions, false, false, SourceCommandLine); + if (!ret.isOK()) { + return ret; + } - try { - po::store(po::command_line_parser(argc, (argc > 0 ? &argv_buffer[0] : NULL)). - options(boostOptions). - positional(boostPositionalOptions). - style(style). - run(), vm); + ret = options.getBoostPositionalOptions(&boostPositionalOptions); + if (!ret.isOK()) { + return ret; + } - ret = addBoostVariablesToEnvironment(vm, options, environment); - if (!ret.isOK()) { - return ret; - } - } - catch (po::multiple_occurrences& e) { - StringBuilder sb; - sb << "Error parsing command line: Multiple occurrences of option \"--" - << e.get_option_name() << "\""; - return Status(ErrorCodes::BadValue, sb.str()); - } - catch (po::error& e) { - StringBuilder sb; - sb << "Error parsing command line: " << e.what(); - return Status(ErrorCodes::BadValue, sb.str()); - } + try { + po::store(po::command_line_parser(argc, (argc > 0 ? &argv_buffer[0] : NULL)) + .options(boostOptions) + .positional(boostPositionalOptions) + .style(style) + .run(), + vm); - // This is needed because "switches" default to false in boost, and we don't want to - // erroneously think that they were present but set to false in a config file. - ret = removeFalseSwitches(options, environment); + ret = addBoostVariablesToEnvironment(vm, options, environment); if (!ret.isOK()) { return ret; } + } catch (po::multiple_occurrences& e) { + StringBuilder sb; + sb << "Error parsing command line: Multiple occurrences of option \"--" + << e.get_option_name() << "\""; + return Status(ErrorCodes::BadValue, sb.str()); + } catch (po::error& e) { + StringBuilder sb; + sb << "Error parsing command line: " << e.what(); + return Status(ErrorCodes::BadValue, sb.str()); + } - return Status::OK(); + // This is needed because "switches" default to false in boost, and we don't want to + // erroneously think that they were present but set to false in a config file. + ret = removeFalseSwitches(options, environment); + if (!ret.isOK()) { + return ret; } - /** - * This function delegates the INI config parsing to boost program_options. - * - * 1. Extract the boost readable option descriptions from the OptionSection - * 2. Passes them to the boost config file parser - * 3. Copy everything from the variables map returned by boost into the Environment - */ - Status OptionsParser::parseINIConfigFile(const OptionSection& options, - const std::string& config, - Environment* environment) { - po::options_description boostOptions; - po::variables_map vm; + return Status::OK(); +} + +/** + * This function delegates the INI config parsing to boost program_options. + * + * 1. Extract the boost readable option descriptions from the OptionSection + * 2. Passes them to the boost config file parser + * 3. Copy everything from the variables map returned by boost into the Environment + */ +Status OptionsParser::parseINIConfigFile(const OptionSection& options, + const std::string& config, + Environment* environment) { + po::options_description boostOptions; + po::variables_map vm; + + Status ret = options.getBoostOptions(&boostOptions, false, false, SourceINIConfig); + if (!ret.isOK()) { + return ret; + } - Status ret = options.getBoostOptions(&boostOptions, false, false, SourceINIConfig); + std::istringstream is(config); + try { + po::store(po::parse_config_file(is, boostOptions), vm); + ret = addBoostVariablesToEnvironment(vm, options, environment); if (!ret.isOK()) { return ret; } - - std::istringstream is(config); - try { - po::store(po::parse_config_file(is, boostOptions), vm); - ret = addBoostVariablesToEnvironment(vm, options, environment); - if (!ret.isOK()) { - return ret; - } - } - catch (po::multiple_occurrences& e) { - StringBuilder sb; - sb << "Error parsing INI config file: Multiple occurrences of option \"" - << e.get_option_name() << "\""; - return Status(ErrorCodes::BadValue, sb.str()); - } - catch (po::error& e) { - StringBuilder sb; - sb << "Error parsing INI config file: " << e.what(); - return Status(ErrorCodes::BadValue, sb.str()); - } - return Status::OK(); + } catch (po::multiple_occurrences& e) { + StringBuilder sb; + sb << "Error parsing INI config file: Multiple occurrences of option \"" + << e.get_option_name() << "\""; + return Status(ErrorCodes::BadValue, sb.str()); + } catch (po::error& e) { + StringBuilder sb; + sb << "Error parsing INI config file: " << e.what(); + return Status(ErrorCodes::BadValue, sb.str()); } + return Status::OK(); +} namespace { - /** - * This function delegates the YAML config parsing to the third party YAML parser. It does no - * error checking other than the parse error checking done by the YAML parser. - */ - Status parseYAMLConfigFile(const std::string& config, - YAML::Node* YAMLConfig) { - - try { - *YAMLConfig = YAML::Load(config); - } catch (const YAML::Exception &e) { - StringBuilder sb; - sb << "Error parsing YAML config file: " << e.what(); - return Status(ErrorCodes::BadValue, sb.str()); - } catch (const std::runtime_error &e) { - StringBuilder sb; - sb << "Unexpected exception parsing YAML config file: " << e.what(); - return Status(ErrorCodes::BadValue, sb.str()); - } - - return Status::OK(); +/** + * This function delegates the YAML config parsing to the third party YAML parser. It does no + * error checking other than the parse error checking done by the YAML parser. + */ +Status parseYAMLConfigFile(const std::string& config, YAML::Node* YAMLConfig) { + try { + *YAMLConfig = YAML::Load(config); + } catch (const YAML::Exception& e) { + StringBuilder sb; + sb << "Error parsing YAML config file: " << e.what(); + return Status(ErrorCodes::BadValue, sb.str()); + } catch (const std::runtime_error& e) { + StringBuilder sb; + sb << "Unexpected exception parsing YAML config file: " << e.what(); + return Status(ErrorCodes::BadValue, sb.str()); } - bool isYAMLConfig(const YAML::Node& config) { - // The YAML parser is very forgiving, and for the INI config files we've parsed so far using - // the YAML parser, the YAML parser just slurps the entire config file into a single string - // rather than erroring out. Thus, we assume that having a scalar (string) as the root node - // means that this is not meant to be a YAML config file, since even a very simple YAML - // config file should be parsed as a Map, and thus "config.IsScalar()" would return false. - // - // This requires more testing, both to ensure that all INI style files get parsed as a - // single string, and to ensure that the user experience does not suffer (in terms of this - // causing confusing error messages for users writing a brand new YAML config file that - // incorrectly triggers this check). - if (config.IsScalar()) { - return false; - } - else { - return true; - } + return Status::OK(); +} + +bool isYAMLConfig(const YAML::Node& config) { + // The YAML parser is very forgiving, and for the INI config files we've parsed so far using + // the YAML parser, the YAML parser just slurps the entire config file into a single string + // rather than erroring out. Thus, we assume that having a scalar (string) as the root node + // means that this is not meant to be a YAML config file, since even a very simple YAML + // config file should be parsed as a Map, and thus "config.IsScalar()" would return false. + // + // This requires more testing, both to ensure that all INI style files get parsed as a + // single string, and to ensure that the user experience does not suffer (in terms of this + // causing confusing error messages for users writing a brand new YAML config file that + // incorrectly triggers this check). + if (config.IsScalar()) { + return false; + } else { + return true; } +} -} // namespace +} // namespace - /** - * Add default values from the given OptionSection to the given Environment - */ - Status OptionsParser::addDefaultValues(const OptionSection& options, - Environment* environment) { - std::map <Key, Value> defaultOptions; +/** + * Add default values from the given OptionSection to the given Environment + */ +Status OptionsParser::addDefaultValues(const OptionSection& options, Environment* environment) { + std::map<Key, Value> defaultOptions; - Status ret = options.getDefaults(&defaultOptions); + Status 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 = environment->setDefault(iterator->first, iterator->second); if (!ret.isOK()) { return ret; } - - typedef std::map<Key, Value>::iterator it_type; - for(it_type iterator = defaultOptions.begin(); - iterator != defaultOptions.end(); iterator++) { - ret = environment->setDefault(iterator->first, iterator->second); - if (!ret.isOK()) { - return ret; - } - } - - return Status::OK(); } - /** - * Reads the entire config file into the output string. This was done this way because the JSON - * parser only takes complete strings, and we were using that to parse the config file before. - * We could redesign the parser to use some kind of streaming interface, but for now this is - * simple and works for the current use case of config files which should be limited in size. - */ - Status OptionsParser::readConfigFile(const std::string& filename, std::string* contents) { - - FILE* config; - config = fopen(filename.c_str(), "r"); - if (config == NULL) { - const int current_errno = errno; - StringBuilder sb; - sb << "Error reading config file: " << strerror(current_errno); - return Status(ErrorCodes::InternalError, sb.str()); - } - ON_BLOCK_EXIT(fclose, config); + return Status::OK(); +} - // Get length of config file by seeking to the end and getting the cursor position - if (fseek(config, 0L, SEEK_END) != 0) { - const int current_errno = errno; - // TODO: Make sure errno is the correct way to do this - // Confirmed that errno gets set in Mac OSX, but not documented - StringBuilder sb; - sb << "Error seeking in config file: " << strerror(current_errno); - return Status(ErrorCodes::InternalError, sb.str()); - } - long configSize = ftell(config); - - // Seek back to the beginning of the file for reading - if (fseek(config, 0L, SEEK_SET) != 0) { - const int current_errno = errno; - // TODO: Make sure errno is the correct way to do this - // Confirmed that errno gets set in Mac OSX, but not documented - StringBuilder sb; - sb << "Error seeking in config file: " << strerror(current_errno); - return Status(ErrorCodes::InternalError, sb.str()); - } +/** + * Reads the entire config file into the output string. This was done this way because the JSON + * parser only takes complete strings, and we were using that to parse the config file before. + * We could redesign the parser to use some kind of streaming interface, but for now this is + * simple and works for the current use case of config files which should be limited in size. + */ +Status OptionsParser::readConfigFile(const std::string& filename, std::string* contents) { + FILE* config; + config = fopen(filename.c_str(), "r"); + if (config == NULL) { + const int current_errno = errno; + StringBuilder sb; + sb << "Error reading config file: " << strerror(current_errno); + return Status(ErrorCodes::InternalError, sb.str()); + } + ON_BLOCK_EXIT(fclose, config); + + // Get length of config file by seeking to the end and getting the cursor position + if (fseek(config, 0L, SEEK_END) != 0) { + const int current_errno = errno; + // TODO: Make sure errno is the correct way to do this + // Confirmed that errno gets set in Mac OSX, but not documented + StringBuilder sb; + sb << "Error seeking in config file: " << strerror(current_errno); + return Status(ErrorCodes::InternalError, sb.str()); + } + long configSize = ftell(config); + + // Seek back to the beginning of the file for reading + if (fseek(config, 0L, SEEK_SET) != 0) { + const int current_errno = errno; + // TODO: Make sure errno is the correct way to do this + // Confirmed that errno gets set in Mac OSX, but not documented + StringBuilder sb; + sb << "Error seeking in config file: " << strerror(current_errno); + return Status(ErrorCodes::InternalError, sb.str()); + } - // Read into a vector first since it's guaranteed to have contiguous storage - std::vector<char> configVector; - configVector.resize(configSize); - - if (configSize > 0) { - long nread = 0; - while (!feof(config) && nread < configSize) { - nread = nread + fread(&configVector[nread], sizeof(char), - configSize - nread, config); - if (ferror(config)) { - const int current_errno = errno; - // TODO: Make sure errno is the correct way to do this - StringBuilder sb; - sb << "Error reading in config file: " << strerror(current_errno); - return Status(ErrorCodes::InternalError, sb.str()); - } + // Read into a vector first since it's guaranteed to have contiguous storage + std::vector<char> configVector; + configVector.resize(configSize); + + if (configSize > 0) { + long nread = 0; + while (!feof(config) && nread < configSize) { + nread = nread + fread(&configVector[nread], sizeof(char), configSize - nread, config); + if (ferror(config)) { + const int current_errno = errno; + // TODO: Make sure errno is the correct way to do this + StringBuilder sb; + sb << "Error reading in config file: " << strerror(current_errno); + return Status(ErrorCodes::InternalError, sb.str()); } - // Resize our config vector to the number of bytes we actually read - configVector.resize(nread); } - - // Copy the vector contents into our result string - *contents = std::string(configVector.begin(), configVector.end()); - - return Status::OK(); + // Resize our config vector to the number of bytes we actually read + configVector.resize(nread); } - /** - * Run the OptionsParser - * - * Overview: - * - * 1. Parse argc and argv using the given OptionSection as a description of expected options - * 2. Check for a "config" argument - * 3. If "config" found, read config file - * 4. Detect config file type (YAML or INI) - * 5. Parse config file using the given OptionSection as a description of expected options - * 6. Add the results to the output Environment in the proper order to ensure correct precedence - */ - Status OptionsParser::run(const OptionSection& options, - const std::vector<std::string>& argv, - const std::map<std::string, std::string>& env, // XXX: Currently unused - Environment* environment) { + // Copy the vector contents into our result string + *contents = std::string(configVector.begin(), configVector.end()); + + return Status::OK(); +} - Environment commandLineEnvironment; - Environment configEnvironment; - Environment composedEnvironment; +/** + * Run the OptionsParser + * + * Overview: + * + * 1. Parse argc and argv using the given OptionSection as a description of expected options + * 2. Check for a "config" argument + * 3. If "config" found, read config file + * 4. Detect config file type (YAML or INI) + * 5. Parse config file using the given OptionSection as a description of expected options + * 6. Add the results to the output Environment in the proper order to ensure correct precedence + */ +Status OptionsParser::run(const OptionSection& options, + const std::vector<std::string>& argv, + const std::map<std::string, std::string>& env, // XXX: Currently unused + Environment* environment) { + Environment commandLineEnvironment; + Environment configEnvironment; + Environment composedEnvironment; + + Status ret = parseCommandLine(options, argv, &commandLineEnvironment); + if (!ret.isOK()) { + return ret; + } - Status ret = parseCommandLine(options, argv, &commandLineEnvironment); + Value config_value; + ret = commandLineEnvironment.get(Key("config"), &config_value); + // We had an error other than "config" not existing in our environment + if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) { + return ret; + } + // "config" exists in our environment + else if (ret.isOK()) { + // Environment::get returns a bad status if config was not set + std::string config_filename; + ret = config_value.get(&config_filename); if (!ret.isOK()) { return ret; } - Value config_value; - ret = commandLineEnvironment.get(Key("config"), &config_value); - // We had an error other than "config" not existing in our environment - if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) { + std::string config_file; + ret = readConfigFile(config_filename, &config_file); + if (!ret.isOK()) { return ret; } - // "config" exists in our environment - else if (ret.isOK()) { - // Environment::get returns a bad status if config was not set - std::string config_filename; - ret = config_value.get(&config_filename); - if (!ret.isOK()) { - return ret; - } + YAML::Node YAMLConfig; + ret = parseYAMLConfigFile(config_file, &YAMLConfig); + if (!ret.isOK()) { + return ret; + } - std::string config_file; - ret = readConfigFile(config_filename, &config_file); + if (isYAMLConfig(YAMLConfig)) { + ret = addYAMLNodesToEnvironment(YAMLConfig, options, "", &configEnvironment); if (!ret.isOK()) { return ret; } - - YAML::Node YAMLConfig; - ret = parseYAMLConfigFile(config_file, &YAMLConfig); + } else { + ret = parseINIConfigFile(options, config_file, &configEnvironment); if (!ret.isOK()) { return ret; } - - if (isYAMLConfig(YAMLConfig)) { - ret = addYAMLNodesToEnvironment(YAMLConfig, options, "", &configEnvironment); - if (!ret.isOK()) { - return ret; - } - } - else { - ret = parseINIConfigFile(options, config_file, &configEnvironment); - if (!ret.isOK()) { - return ret; - } - } - } - - // 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. - // NOTE: We must add our configEnvironment compositions first since we have a StringMap type - // in which some options can be overridden by the command line. - ret = addCompositions(options, configEnvironment, &composedEnvironment); - if (!ret.isOK()) { - return ret; } + } - ret = addCompositions(options, commandLineEnvironment, &composedEnvironment); - if (!ret.isOK()) { - return ret; - } + // 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. + // NOTE: We must add our configEnvironment compositions first since we have a StringMap type + // in which some options can be overridden by the command line. + ret = addCompositions(options, configEnvironment, &composedEnvironment); + if (!ret.isOK()) { + return ret; + } - // Add the default values to our resulting environment - ret = addDefaultValues(options, environment); - if (!ret.isOK()) { - return ret; - } + ret = addCompositions(options, commandLineEnvironment, &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(configEnvironment); - if (!ret.isOK()) { - return ret; - } - ret = environment->setAll(commandLineEnvironment); - if (!ret.isOK()) { - return ret; - } + // Add the default values to our resulting environment + ret = addDefaultValues(options, environment); + if (!ret.isOK()) { + return ret; + } - // Add this last because it has all the composable options aggregated over different - // sources. For example, if we have a StringMap type with some values set on the command - // line and some values set in config files, we want to make sure to get them all. This - // should not override any non composable options, since composedEnvironment should not have - // them set. See the addCompositions function for more details. - ret = environment->setAll(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(configEnvironment); + if (!ret.isOK()) { + return ret; + } + ret = environment->setAll(commandLineEnvironment); + if (!ret.isOK()) { + return ret; + } - // Add the constraints from our options to the result environment - ret = addConstraints(options, environment); - if (!ret.isOK()) { - return ret; - } + // Add this last because it has all the composable options aggregated over different + // sources. For example, if we have a StringMap type with some values set on the command + // line and some values set in config files, we want to make sure to get them all. This + // should not override any non composable options, since composedEnvironment should not have + // them set. See the addCompositions function for more details. + ret = environment->setAll(composedEnvironment); + if (!ret.isOK()) { + return ret; + } - return Status::OK(); + // Add the constraints from our options to the result environment + ret = addConstraints(options, environment); + if (!ret.isOK()) { + return ret; } -} // namespace optionenvironment -} // namespace mongo + return Status::OK(); +} + +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/options_parser.h b/src/mongo/util/options_parser/options_parser.h index 8e277f25352..9c3752f1660 100644 --- a/src/mongo/util/options_parser/options_parser.h +++ b/src/mongo/util/options_parser/options_parser.h @@ -36,87 +36,88 @@ namespace mongo { namespace optionenvironment { - class Environment; - class OptionSection; - class Value; +class Environment; +class OptionSection; +class Value; - /** Handles parsing of the command line as well as YAML and INI config files. Takes an - * OptionSection instance that describes the allowed options, parses argv (env not yet - * supported), and populates an Environment with the results. - * - * Usage: - * - * namespace moe = mongo::optionenvironment; - * - * moe::OptionsParser parser; - * moe::Environment environment; - * moe::OptionSection options; - * - * // Register our allowed options with our OptionSection - * options.addOptionChaining("help", "help", moe::Switch, "Display Help"); - * options.addOptionChaining("port", "port", moe::Int, "Port"); - * - * // Run the parser - * Status ret = parser.run(options, argv, env, &environment); - * if (!ret.isOK()) { - * cerr << options.helpString() << std::endl; - * exit(EXIT_FAILURE); - * } - * - * bool displayHelp; - * ret = environment.get(moe::Key("help"), &displayHelp); - * if (!ret.isOK()) { - * // Help is a switch, so it should always be set - * cout << "Should not get here" << std::endl; - * exit(EXIT_FAILURE); - * } - * if (displayHelp) { - * cout << options.helpString() << std::endl; - * exit(EXIT_SUCCESS); - * } +/** Handles parsing of the command line as well as YAML and INI config files. Takes an + * OptionSection instance that describes the allowed options, parses argv (env not yet + * supported), and populates an Environment with the results. + * + * Usage: + * + * namespace moe = mongo::optionenvironment; + * + * moe::OptionsParser parser; + * moe::Environment environment; + * moe::OptionSection options; + * + * // Register our allowed options with our OptionSection + * options.addOptionChaining("help", "help", moe::Switch, "Display Help"); + * options.addOptionChaining("port", "port", moe::Int, "Port"); + * + * // Run the parser + * Status ret = parser.run(options, argv, env, &environment); + * if (!ret.isOK()) { + * cerr << options.helpString() << std::endl; + * exit(EXIT_FAILURE); + * } + * + * bool displayHelp; + * ret = environment.get(moe::Key("help"), &displayHelp); + * if (!ret.isOK()) { + * // Help is a switch, so it should always be set + * cout << "Should not get here" << std::endl; + * exit(EXIT_FAILURE); + * } + * if (displayHelp) { + * cout << options.helpString() << std::endl; + * exit(EXIT_SUCCESS); + * } + * + * // Get the value of port from the environment + * int port = 27017; + * ret = environment.get(moe::Key("port"), &port); + * if (ret.isOK()) { + * // We have overridden port here, otherwise it stays as the default. + * } + */ +class OptionsParser { +public: + OptionsParser() {} + virtual ~OptionsParser() {} + + /** Handles parsing of the command line as well as YAML and INI config files. The + * OptionSection be a description of the allowed options. This function populates the + * given Environment with the results of parsing the command line and or config files but + * does not call validate on the Environment. * - * // Get the value of port from the environment - * int port = 27017; - * ret = environment.get(moe::Key("port"), &port); - * if (ret.isOK()) { - * // We have overridden port here, otherwise it stays as the default. - * } + * The only special option is the "config" option. This function will check if the + * "config" option was set on the command line and if so attempt to read the given config + * file. For binaries that do not support config files, the "config" option should not be + * registered in the OptionSection. */ - class OptionsParser { - public: - OptionsParser() { } - virtual ~OptionsParser() { } - - /** Handles parsing of the command line as well as YAML and INI config files. The - * OptionSection be a description of the allowed options. This function populates the - * given Environment with the results of parsing the command line and or config files but - * does not call validate on the Environment. - * - * The only special option is the "config" option. This function will check if the - * "config" option was set on the command line and if so attempt to read the given config - * file. For binaries that do not support config files, the "config" option should not be - * registered in the OptionSection. - */ - Status run(const OptionSection&, - const std::vector<std::string>& argv, - const std::map<std::string, std::string>& env, - Environment*); + Status run(const OptionSection&, + const std::vector<std::string>& argv, + const std::map<std::string, std::string>& env, + Environment*); - private: - /** Handles parsing of the command line and adds the results to the given Environment */ - Status parseCommandLine(const OptionSection&, - const std::vector<std::string>& argv, Environment*); +private: + /** Handles parsing of the command line and adds the results to the given Environment */ + Status parseCommandLine(const OptionSection&, + const std::vector<std::string>& argv, + Environment*); - /** Handles parsing of an INI config std::string and adds the results to the given Environment */ - Status parseINIConfigFile(const OptionSection&, const std::string& config, Environment*); + /** Handles parsing of an INI config std::string and adds the results to the given Environment */ + Status parseINIConfigFile(const OptionSection&, const std::string& config, Environment*); - /** Gets defaults from the OptionSection and adds them to the given Environment */ - Status addDefaultValues(const OptionSection&, Environment*); + /** Gets defaults from the OptionSection and adds them to the given Environment */ + Status addDefaultValues(const OptionSection&, Environment*); - /** Reads the given config file into the output string. This function is virtual for - * testing purposes only. */ - virtual Status readConfigFile(const std::string& filename, std::string*); - }; + /** Reads the given config file into the output string. This function is virtual for + * testing purposes only. */ + virtual Status readConfigFile(const std::string& filename, std::string*); +}; -} // namespace optionenvironment -} // namespace mongo +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/options_parser_init.cpp b/src/mongo/util/options_parser/options_parser_init.cpp index e703d3bdd49..f75c98c603a 100644 --- a/src/mongo/util/options_parser/options_parser_init.cpp +++ b/src/mongo/util/options_parser/options_parser_init.cpp @@ -42,17 +42,15 @@ namespace optionenvironment { MONGO_STARTUP_OPTIONS_PARSE(StartupOptions)(InitializerContext* context) { OptionsParser parser; - Status ret = parser.run(startupOptions, context->args(), context->env(), - &startupOptionsParsed); + Status ret = parser.run(startupOptions, context->args(), context->env(), &startupOptionsParsed); if (!ret.isOK()) { std::cerr << ret.reason() << std::endl; // TODO: Figure out if there's a use case for this help message ever being different - std::cerr << "try '" << context->args()[0] - << " --help' for more information" << std::endl; + std::cerr << "try '" << context->args()[0] << " --help' for more information" << std::endl; quickExit(EXIT_BADOPTIONS); } return Status::OK(); } -} // namespace optionenvironment -} // namespace mongo +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/options_parser_test.cpp b/src/mongo/util/options_parser/options_parser_test.cpp index cde7f4b2896..738bcb90d21 100644 --- a/src/mongo/util/options_parser/options_parser_test.cpp +++ b/src/mongo/util/options_parser/options_parser_test.cpp @@ -39,3651 +39,3707 @@ namespace { - using mongo::ErrorCodes; - using mongo::Status; +using mongo::ErrorCodes; +using mongo::Status; - namespace moe = mongo::optionenvironment; +namespace moe = mongo::optionenvironment; #define TEST_CONFIG_PATH(x) "src/mongo/util/options_parser/test_config_files/" x - class OptionsParserTester : public moe::OptionsParser { - public: - Status readConfigFile(const std::string& filename, std::string* config) { - if (filename != _filename) { - ::mongo::StringBuilder sb; - sb << "Parser using filename: " << filename << - " which does not match expected filename: " << _filename; - return Status(ErrorCodes::InternalError, sb.str()); - } - *config = _config; - return Status::OK(); - } - void setConfig(const std::string& filename, const std::string& config) { - _filename = filename; - _config = config; - } - private: - std::string _filename; - std::string _config; - }; - - TEST(Registration, EmptySingleName) { - moe::OptionSection testOpts; - try { - testOpts.addOptionChaining("dup", "", moe::Switch, "dup"); - testOpts.addOptionChaining("new", "", moe::Switch, "dup"); - } - catch (::mongo::DBException &e) { - ::mongo::StringBuilder sb; - sb << "Was not able to register two options with empty single name: " << e.what(); - FAIL(sb.str()); - } - - // This should fail now, because we didn't specify that these options were not valid in the - // INI config or on the command line - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - moe::OptionsParser parser; - moe::Environment environment; - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - moe::OptionSection testOptsValid; - try { - testOptsValid.addOptionChaining("dup", "", moe::Switch, "dup") - .setSources(moe::SourceYAMLConfig); - testOptsValid.addOptionChaining("new", "", moe::Switch, "dup") - .setSources(moe::SourceYAMLConfig); - } - catch (::mongo::DBException &e) { +class OptionsParserTester : public moe::OptionsParser { +public: + Status readConfigFile(const std::string& filename, std::string* config) { + if (filename != _filename) { ::mongo::StringBuilder sb; - sb << "Was not able to register two options with empty single name" << e.what(); - FAIL(sb.str()); - } - - // This should pass now, because we specified that these options were not valid in the INI - // config or on the command line - ASSERT_OK(parser.run(testOptsValid, argv, env_map, &environment)); - } - - TEST(Registration, DuplicateSingleName) { - moe::OptionSection testOpts; - try { - testOpts.addOptionChaining("dup", "dup", moe::Switch, "dup"); - testOpts.addOptionChaining("new", "dup", moe::Switch, "dup"); - FAIL("Was able to register duplicate single name"); - } - catch (::mongo::DBException&) { - } - } - - TEST(Registration, DuplicateDottedName) { - moe::OptionSection testOpts; - try { - testOpts.addOptionChaining("dup", "dup", moe::Switch, "dup"); - testOpts.addOptionChaining("dup", "new", moe::Switch, "dup"); - FAIL("Was able to register duplicate single name"); - } - catch (::mongo::DBException&) { - } - } - - TEST(Registration, DuplicatePositional) { - moe::OptionSection testOpts; - try { - testOpts.addOptionChaining("positional", "positional", moe::Int, "Positional") - .positional(1, 1); - testOpts.addOptionChaining("positional", "positional", moe::Int, "Positional") - .positional(1, 1); - FAIL("Was able to register duplicate positional option"); - } - catch (::mongo::DBException&) { - } - } - - TEST(Registration, BadRangesPositional) { - moe::OptionSection testOpts; - try { - testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") - .positional(-1, 1); - FAIL("Was able to register positional with negative start for range"); - } - catch (::mongo::DBException&) { - } - try { - testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") - .positional(2, 1); - FAIL("Was able to register positional with start of range larger than end"); - } - catch (::mongo::DBException&) { - } - try { - testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") - .positional(1, -2); - FAIL("Was able to register positional with bad end of range"); - } - catch (::mongo::DBException&) { - } - try { - testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") - .positional(0, 1); - FAIL("Was able to register positional with bad start of range"); - } - catch (::mongo::DBException&) { - } - try { - testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") - .positional(1, 2); - FAIL("Was able to register multi valued positional with non StringVector type"); - } - catch (::mongo::DBException&) { - } - } - - TEST(Registration, DefaultValueWrongType) { - moe::OptionSection testOpts; - try { - testOpts.addOptionChaining("port", "port", moe::Int, "Port") - .setDefault(moe::Value("String")); - FAIL("Was able to register default value with wrong type"); - } - catch (::mongo::DBException&) { - } - } - - TEST(Registration, ImplicitValueWrongType) { - moe::OptionSection testOpts; - try { - testOpts.addOptionChaining("port", "port", moe::Int, "Port") - .setImplicit(moe::Value("String")); - FAIL("Was able to register implicit value with wrong type"); - } - catch (::mongo::DBException&) { - } - } - - TEST(Registration, ComposableNotVectorOrMap) { - moe::OptionSection testOpts; - try { - testOpts.addOptionChaining("setParameter", "setParameter", moe::String, - "Multiple Values").composing(); - FAIL("Was able to register composable option with wrong type"); - } - catch (::mongo::DBException&) { - } - } - - TEST(Registration, ComposableWithImplicit) { - moe::OptionSection testOpts; - try { - std::vector<std::string> implicitVal; - implicitVal.push_back("implicit"); - testOpts.addOptionChaining("setParameter", "setParameter", moe::StringVector, - "Multiple Values") - .setImplicit(moe::Value(implicitVal)) - .composing(); - FAIL("Was able to register composable option with implicit value"); - } - catch (::mongo::DBException&) { - } - - try { - std::vector<std::string> implicitVal; - implicitVal.push_back("implicit"); - testOpts.addOptionChaining("setParameter", "setParameter", moe::StringVector, - "Multiple Values") - .composing() - .setImplicit(moe::Value(implicitVal)); - FAIL("Was able to set implicit value on composable option"); - } - catch (::mongo::DBException&) { - } - } - - TEST(Registration, ComposableWithDefault) { - moe::OptionSection testOpts; - try { - std::vector<std::string> defaultVal; - defaultVal.push_back("default"); - testOpts.addOptionChaining("setParameter", "setParameter", moe::StringVector, - "Multiple Values") - .setDefault(moe::Value(defaultVal)) - .composing(); - FAIL("Was able to register composable option with default value"); - } - catch (::mongo::DBException&) { - } - - try { - std::vector<std::string> defaultVal; - defaultVal.push_back("default"); - testOpts.addOptionChaining("setParameter", "setParameter", moe::StringVector, - "Multiple Values") - .composing() - .setDefault(moe::Value(defaultVal)); - FAIL("Was able to set default value on composable option"); - } - catch (::mongo::DBException&) { - } - } - - TEST(Registration, NumericRangeConstraint) { - moe::OptionSection testOpts; - try { - std::vector<std::string> defaultVal; - defaultVal.push_back("default"); - testOpts.addOptionChaining("port", "port", moe::String, "Port") - .validRange(1000, 65535); - FAIL("Was able to register non numeric option with constraint on range"); - } - catch (::mongo::DBException&) { - } - } - - TEST(Registration, StringFormatConstraint) { - moe::OptionSection testOpts; - try { - testOpts.addOptionChaining("port", "port", moe::Int, "Port") - .format("[0-9]*", "[0-9]*"); - FAIL("Was able to register non string option with constraint on format"); + sb << "Parser using filename: " << filename + << " which does not match expected filename: " << _filename; + return Status(ErrorCodes::InternalError, sb.str()); } - catch (::mongo::DBException&) { - } - } - - TEST(Parsing, Good) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--port"); - argv.push_back("5"); - argv.push_back("--help"); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("help"), &value)); - ASSERT_OK(environment.get(moe::Key("port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - } - - TEST(Parsing, SubSection) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - moe::OptionSection subSection("Section Name"); - - subSection.addOptionChaining("port", "port", moe::Int, "Port"); - testOpts.addSection(subSection); - - 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; - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - } - - TEST(Parsing, StringVector) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--multival"); - argv.push_back("val1"); - argv.push_back("--multival"); - argv.push_back("val2"); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("multival"), &value)); - std::vector<std::string> multival; - std::vector<std::string>::iterator multivalit; - ASSERT_OK(value.get(&multival)); - multivalit = multival.begin(); - ASSERT_EQUALS(*multivalit, "val1"); - multivalit++; - ASSERT_EQUALS(*multivalit, "val2"); - } - - TEST(Parsing, StringMap) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--multival"); - argv.push_back("key1=value1"); - argv.push_back("--multival"); - argv.push_back("key2=value2"); - argv.push_back("--multival"); - argv.push_back("key3="); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("multival"), &value)); - std::map<std::string, std::string> multival; - std::map<std::string, std::string>::iterator multivalit; - ASSERT_OK(value.get(&multival)); - multivalit = multival.begin(); - ASSERT_EQUALS(multivalit->first, "key1"); - ASSERT_EQUALS(multivalit->second, "value1"); - multivalit++; - ASSERT_EQUALS(multivalit->first, "key2"); - ASSERT_EQUALS(multivalit->second, "value2"); - multivalit++; - ASSERT_EQUALS(multivalit->first, "key3"); - ASSERT_EQUALS(multivalit->second, ""); - } - - TEST(Parsing, StringMapDuplicateKey) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--multival"); - argv.push_back("key1=value1"); - argv.push_back("--multival"); - argv.push_back("key1=value2"); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(Parsing, Positional) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::String, "Positional") - .positional(1, 1); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional"); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("positional"), &value)); - std::string positional; - ASSERT_OK(value.get(&positional)); - ASSERT_EQUALS(positional, "positional"); - } - - TEST(Parsing, PositionalTooMany) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::String, "Positional") - .positional(1, 1); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional"); - argv.push_back("extrapositional"); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(Parsing, PositionalAndFlag) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::String, "Positional") - .positional(1, 1); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional"); - argv.push_back("--port"); - argv.push_back("5"); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("positional"), &value)); - std::string positional; - ASSERT_OK(value.get(&positional)); - ASSERT_EQUALS(positional, "positional"); - ASSERT_OK(environment.get(moe::Key("port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - } - - TEST(Parsing, PositionalMultiple) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") - .positional(1, 2); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional1"); - argv.push_back("positional2"); - 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("positional"), &value)); - std::vector<std::string> positional; - ASSERT_OK(value.get(&positional)); - std::vector<std::string>::iterator positionalit = positional.begin(); - ASSERT_EQUALS(*positionalit, "positional1"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional2"); - } - - TEST(Parsing, PositionalMultipleExtra) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") - .positional(1, 2); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional1"); - argv.push_back("positional2"); - argv.push_back("positional2"); - std::map<std::string, std::string> env_map; - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(Parsing, PositionalMultipleUnlimited) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") - .positional(1, -1); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional1"); - argv.push_back("positional2"); - argv.push_back("positional3"); - argv.push_back("positional4"); - argv.push_back("positional5"); - 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("positional"), &value)); - std::vector<std::string> positional; - ASSERT_OK(value.get(&positional)); - std::vector<std::string>::iterator positionalit = positional.begin(); - ASSERT_EQUALS(*positionalit, "positional1"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional2"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional3"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional4"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional5"); - } - - TEST(Parsing, PositionalMultipleAndFlag) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") - .positional(1, 2); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional1"); - argv.push_back("--port"); - argv.push_back("5"); - argv.push_back("positional2"); - 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("positional"), &value)); - std::vector<std::string> positional; - ASSERT_OK(value.get(&positional)); - std::vector<std::string>::iterator positionalit = positional.begin(); - ASSERT_EQUALS(*positionalit, "positional1"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional2"); - ASSERT_OK(environment.get(moe::Key("port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - } - - TEST(Parsing, NeedArg) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--port"); - std::map<std::string, std::string> env_map; - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(Parsing, BadArg) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--port"); - argv.push_back("string"); - std::map<std::string, std::string> env_map; - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(Parsing, ExtraArg) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--help"); - argv.push_back("string"); - std::map<std::string, std::string> env_map; - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + *config = _config; + return Status::OK(); + } + void setConfig(const std::string& filename, const std::string& config) { + _filename = filename; + _config = config; + } + +private: + std::string _filename; + std::string _config; +}; + +TEST(Registration, EmptySingleName) { + moe::OptionSection testOpts; + try { + testOpts.addOptionChaining("dup", "", moe::Switch, "dup"); + testOpts.addOptionChaining("new", "", moe::Switch, "dup"); + } catch (::mongo::DBException& e) { + ::mongo::StringBuilder sb; + sb << "Was not able to register two options with empty single name: " << e.what(); + FAIL(sb.str()); + } + + // This should fail now, because we didn't specify that these options were not valid in the + // INI config or on the command line + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + moe::OptionsParser parser; + moe::Environment environment; + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + moe::OptionSection testOptsValid; + try { + testOptsValid.addOptionChaining("dup", "", moe::Switch, "dup") + .setSources(moe::SourceYAMLConfig); + testOptsValid.addOptionChaining("new", "", moe::Switch, "dup") + .setSources(moe::SourceYAMLConfig); + } catch (::mongo::DBException& e) { + ::mongo::StringBuilder sb; + sb << "Was not able to register two options with empty single name" << e.what(); + FAIL(sb.str()); + } + + // This should pass now, because we specified that these options were not valid in the INI + // config or on the command line + ASSERT_OK(parser.run(testOptsValid, argv, env_map, &environment)); +} + +TEST(Registration, DuplicateSingleName) { + moe::OptionSection testOpts; + try { + testOpts.addOptionChaining("dup", "dup", moe::Switch, "dup"); + testOpts.addOptionChaining("new", "dup", moe::Switch, "dup"); + FAIL("Was able to register duplicate single name"); + } catch (::mongo::DBException&) { + } +} + +TEST(Registration, DuplicateDottedName) { + moe::OptionSection testOpts; + try { + testOpts.addOptionChaining("dup", "dup", moe::Switch, "dup"); + testOpts.addOptionChaining("dup", "new", moe::Switch, "dup"); + FAIL("Was able to register duplicate single name"); + } catch (::mongo::DBException&) { + } +} + +TEST(Registration, DuplicatePositional) { + moe::OptionSection testOpts; + try { + testOpts.addOptionChaining("positional", "positional", moe::Int, "Positional") + .positional(1, 1); + testOpts.addOptionChaining("positional", "positional", moe::Int, "Positional") + .positional(1, 1); + FAIL("Was able to register duplicate positional option"); + } catch (::mongo::DBException&) { + } +} + +TEST(Registration, BadRangesPositional) { + moe::OptionSection testOpts; + try { + testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") + .positional(-1, 1); + FAIL("Was able to register positional with negative start for range"); + } catch (::mongo::DBException&) { } - - TEST(Parsing, DefaultValue) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port").setDefault(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); + try { + testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") + .positional(2, 1); + FAIL("Was able to register positional with start of range larger than end"); + } catch (::mongo::DBException&) { } - - TEST(Parsing, DefaultValueOverride) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port").setDefault(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); + try { + testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") + .positional(1, -2); + FAIL("Was able to register positional with bad end of range"); + } catch (::mongo::DBException&) { } - - TEST(Parsing, DefaultValuesNotInBSON) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); - testOpts.addOptionChaining("val1", "val1", moe::Int, "Val1").setDefault(moe::Value(5)); - testOpts.addOptionChaining("val2", "val2", moe::Int, "Val2").setDefault(moe::Value(5)); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--val1"); - argv.push_back("6"); - std::map<std::string, std::string> env_map; - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - - mongo::BSONObj expected = BSON("val1" << 6); - ASSERT_EQUALS(expected, environment.toBSON()); + try { + testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") + .positional(0, 1); + FAIL("Was able to register positional with bad start of range"); + } catch (::mongo::DBException&) { } - - TEST(Parsing, ImplicitValue) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port") - .setDefault(moe::Value(6)).setImplicit(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); + try { + testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") + .positional(1, 2); + FAIL("Was able to register multi valued positional with non StringVector type"); + } catch (::mongo::DBException&) { } +} - TEST(Parsing, ImplicitValueDefault) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); +TEST(Registration, DefaultValueWrongType) { + moe::OptionSection testOpts; + try { testOpts.addOptionChaining("port", "port", moe::Int, "Port") - .setDefault(moe::Value(6)).setImplicit(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); + .setDefault(moe::Value("String")); + FAIL("Was able to register default value with wrong type"); + } catch (::mongo::DBException&) { } +} - TEST(Parsing, ImplicitValueOverride) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); +TEST(Registration, ImplicitValueWrongType) { + moe::OptionSection testOpts; + try { testOpts.addOptionChaining("port", "port", moe::Int, "Port") - .setDefault(moe::Value(6)).setImplicit(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(Parsing, ShortName) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("help", "help,h", moe::Switch, "Display help"); - testOpts.addOptionChaining("port", "port,p", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("-p"); - argv.push_back("5"); - argv.push_back("-h"); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("help"), &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; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("opt", "opt,o", moe::Switch, "first opt"); - testOpts.addOptionChaining("arg", "arg,a", moe::Switch, "first arg"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("-oa"); - std::map<std::string, std::string> env_map; - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(Style, NoGuessing) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--hel"); - std::map<std::string, std::string> env_map; - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(Style, LongDisguises) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("-help"); - 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("help"), &value)); - bool help; - ASSERT_OK(value.get(&help)); - ASSERT_EQUALS(help, true); - } - - TEST(Style, Verbosity) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("v", "verbose,v", moe::Switch, - "be more verbose (include multiple times for more verbosity e.g. -vvvvv)"); - - /* support for -vv -vvvv etc. */ - for (std::string s = "vv"; s.length() <= 12; s.append("v")) { - testOpts.addOptionChaining(s.c_str(), s.c_str(), moe::Switch, - "higher verbosity levels (hidden)").hidden(); - } - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("-vvvvvv"); - std::map<std::string, std::string> env_map; - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - - moe::Value value; - for (std::string s = "vv"; s.length() <= 12; s.append("v")) { - if (s.length() == 6) { - ASSERT_OK(environment.get(moe::Key(s), &value)); - bool verbose; - ASSERT_OK(value.get(&verbose)); - ASSERT_EQUALS(verbose, true); - } - else { - ASSERT_NOT_OK(environment.get(moe::Key(s), &value)); - } + .setImplicit(moe::Value("String")); + FAIL("Was able to register implicit value with wrong type"); + } catch (::mongo::DBException&) { + } +} + +TEST(Registration, ComposableNotVectorOrMap) { + moe::OptionSection testOpts; + try { + testOpts.addOptionChaining("setParameter", "setParameter", moe::String, "Multiple Values") + .composing(); + FAIL("Was able to register composable option with wrong type"); + } catch (::mongo::DBException&) { + } +} + +TEST(Registration, ComposableWithImplicit) { + moe::OptionSection testOpts; + try { + std::vector<std::string> implicitVal; + implicitVal.push_back("implicit"); + testOpts.addOptionChaining( + "setParameter", "setParameter", moe::StringVector, "Multiple Values") + .setImplicit(moe::Value(implicitVal)) + .composing(); + FAIL("Was able to register composable option with implicit value"); + } catch (::mongo::DBException&) { + } + + try { + std::vector<std::string> implicitVal; + implicitVal.push_back("implicit"); + testOpts.addOptionChaining( + "setParameter", "setParameter", moe::StringVector, "Multiple Values") + .composing() + .setImplicit(moe::Value(implicitVal)); + FAIL("Was able to set implicit value on composable option"); + } catch (::mongo::DBException&) { + } +} + +TEST(Registration, ComposableWithDefault) { + moe::OptionSection testOpts; + try { + std::vector<std::string> defaultVal; + defaultVal.push_back("default"); + testOpts.addOptionChaining( + "setParameter", "setParameter", moe::StringVector, "Multiple Values") + .setDefault(moe::Value(defaultVal)) + .composing(); + FAIL("Was able to register composable option with default value"); + } catch (::mongo::DBException&) { + } + + try { + std::vector<std::string> defaultVal; + defaultVal.push_back("default"); + testOpts.addOptionChaining( + "setParameter", "setParameter", moe::StringVector, "Multiple Values") + .composing() + .setDefault(moe::Value(defaultVal)); + FAIL("Was able to set default value on composable option"); + } catch (::mongo::DBException&) { + } +} + +TEST(Registration, NumericRangeConstraint) { + moe::OptionSection testOpts; + try { + std::vector<std::string> defaultVal; + defaultVal.push_back("default"); + testOpts.addOptionChaining("port", "port", moe::String, "Port").validRange(1000, 65535); + FAIL("Was able to register non numeric option with constraint on range"); + } catch (::mongo::DBException&) { + } +} + +TEST(Registration, StringFormatConstraint) { + moe::OptionSection testOpts; + try { + testOpts.addOptionChaining("port", "port", moe::Int, "Port").format("[0-9]*", "[0-9]*"); + FAIL("Was able to register non string option with constraint on format"); + } catch (::mongo::DBException&) { + } +} + +TEST(Parsing, Good) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--port"); + argv.push_back("5"); + argv.push_back("--help"); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("help"), &value)); + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); +} + +TEST(Parsing, SubSection) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + moe::OptionSection subSection("Section Name"); + + subSection.addOptionChaining("port", "port", moe::Int, "Port"); + testOpts.addSection(subSection); + + 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; + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); +} + +TEST(Parsing, StringVector) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--multival"); + argv.push_back("val1"); + argv.push_back("--multival"); + argv.push_back("val2"); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("multival"), &value)); + std::vector<std::string> multival; + std::vector<std::string>::iterator multivalit; + ASSERT_OK(value.get(&multival)); + multivalit = multival.begin(); + ASSERT_EQUALS(*multivalit, "val1"); + multivalit++; + ASSERT_EQUALS(*multivalit, "val2"); +} + +TEST(Parsing, StringMap) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--multival"); + argv.push_back("key1=value1"); + argv.push_back("--multival"); + argv.push_back("key2=value2"); + argv.push_back("--multival"); + argv.push_back("key3="); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("multival"), &value)); + std::map<std::string, std::string> multival; + std::map<std::string, std::string>::iterator multivalit; + ASSERT_OK(value.get(&multival)); + multivalit = multival.begin(); + ASSERT_EQUALS(multivalit->first, "key1"); + ASSERT_EQUALS(multivalit->second, "value1"); + multivalit++; + ASSERT_EQUALS(multivalit->first, "key2"); + ASSERT_EQUALS(multivalit->second, "value2"); + multivalit++; + ASSERT_EQUALS(multivalit->first, "key3"); + ASSERT_EQUALS(multivalit->second, ""); +} + +TEST(Parsing, StringMapDuplicateKey) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--multival"); + argv.push_back("key1=value1"); + argv.push_back("--multival"); + argv.push_back("key1=value2"); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(Parsing, Positional) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::String, "Positional") + .positional(1, 1); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional"); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("positional"), &value)); + std::string positional; + ASSERT_OK(value.get(&positional)); + ASSERT_EQUALS(positional, "positional"); +} + +TEST(Parsing, PositionalTooMany) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::String, "Positional") + .positional(1, 1); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional"); + argv.push_back("extrapositional"); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(Parsing, PositionalAndFlag) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::String, "Positional") + .positional(1, 1); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional"); + argv.push_back("--port"); + argv.push_back("5"); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("positional"), &value)); + std::string positional; + ASSERT_OK(value.get(&positional)); + ASSERT_EQUALS(positional, "positional"); + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); +} + +TEST(Parsing, PositionalMultiple) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") + .positional(1, 2); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional1"); + argv.push_back("positional2"); + 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("positional"), &value)); + std::vector<std::string> positional; + ASSERT_OK(value.get(&positional)); + std::vector<std::string>::iterator positionalit = positional.begin(); + ASSERT_EQUALS(*positionalit, "positional1"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional2"); +} + +TEST(Parsing, PositionalMultipleExtra) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") + .positional(1, 2); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional1"); + argv.push_back("positional2"); + argv.push_back("positional2"); + std::map<std::string, std::string> env_map; + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(Parsing, PositionalMultipleUnlimited) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") + .positional(1, -1); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional1"); + argv.push_back("positional2"); + argv.push_back("positional3"); + argv.push_back("positional4"); + argv.push_back("positional5"); + 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("positional"), &value)); + std::vector<std::string> positional; + ASSERT_OK(value.get(&positional)); + std::vector<std::string>::iterator positionalit = positional.begin(); + ASSERT_EQUALS(*positionalit, "positional1"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional2"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional3"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional4"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional5"); +} + +TEST(Parsing, PositionalMultipleAndFlag) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") + .positional(1, 2); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional1"); + argv.push_back("--port"); + argv.push_back("5"); + argv.push_back("positional2"); + 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("positional"), &value)); + std::vector<std::string> positional; + ASSERT_OK(value.get(&positional)); + std::vector<std::string>::iterator positionalit = positional.begin(); + ASSERT_EQUALS(*positionalit, "positional1"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional2"); + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); +} + +TEST(Parsing, NeedArg) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--port"); + std::map<std::string, std::string> env_map; + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(Parsing, BadArg) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--port"); + argv.push_back("string"); + std::map<std::string, std::string> env_map; + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(Parsing, ExtraArg) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--help"); + argv.push_back("string"); + std::map<std::string, std::string> env_map; + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(Parsing, DefaultValue) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port").setDefault(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.addOptionChaining("help", "help", moe::Switch, "Display help"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port").setDefault(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, DefaultValuesNotInBSON) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); + testOpts.addOptionChaining("val1", "val1", moe::Int, "Val1").setDefault(moe::Value(5)); + testOpts.addOptionChaining("val2", "val2", moe::Int, "Val2").setDefault(moe::Value(5)); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--val1"); + argv.push_back("6"); + std::map<std::string, std::string> env_map; + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + + mongo::BSONObj expected = BSON("val1" << 6); + ASSERT_EQUALS(expected, environment.toBSON()); +} + +TEST(Parsing, ImplicitValue) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port") + .setDefault(moe::Value(6)) + .setImplicit(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.addOptionChaining("help", "help", moe::Switch, "Display help"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port") + .setDefault(moe::Value(6)) + .setImplicit(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.addOptionChaining("help", "help", moe::Switch, "Display help"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port") + .setDefault(moe::Value(6)) + .setImplicit(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(Parsing, ShortName) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("help", "help,h", moe::Switch, "Display help"); + testOpts.addOptionChaining("port", "port,p", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("-p"); + argv.push_back("5"); + argv.push_back("-h"); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("help"), &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; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("opt", "opt,o", moe::Switch, "first opt"); + testOpts.addOptionChaining("arg", "arg,a", moe::Switch, "first arg"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("-oa"); + std::map<std::string, std::string> env_map; + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(Style, NoGuessing) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--hel"); + std::map<std::string, std::string> env_map; + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(Style, LongDisguises) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("help", "help", moe::Switch, "Display help"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("-help"); + 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("help"), &value)); + bool help; + ASSERT_OK(value.get(&help)); + ASSERT_EQUALS(help, true); +} + +TEST(Style, Verbosity) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining( + "v", + "verbose,v", + moe::Switch, + "be more verbose (include multiple times for more verbosity e.g. -vvvvv)"); + + /* support for -vv -vvvv etc. */ + for (std::string s = "vv"; s.length() <= 12; s.append("v")) { + testOpts.addOptionChaining( + s.c_str(), s.c_str(), moe::Switch, "higher verbosity levels (hidden)") + .hidden(); + } + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("-vvvvvv"); + std::map<std::string, std::string> env_map; + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + + moe::Value value; + for (std::string s = "vv"; s.length() <= 12; s.append("v")) { + if (s.length() == 6) { + ASSERT_OK(environment.get(moe::Key(s), &value)); + bool verbose; + ASSERT_OK(value.get(&verbose)); + ASSERT_EQUALS(verbose, true); + } else { + ASSERT_NOT_OK(environment.get(moe::Key(s), &value)); } } - - TEST(INIConfigFile, Basic) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - 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=5"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - moe::Value value; - ASSERT_OK(environment.get(moe::Key("port"), &value)); +} + +TEST(INIConfigFile, Basic) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + 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=5"); + + 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(INIConfigFile, Empty) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + + 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", ""); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(INIConfigFile, Override) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("default.conf"); + argv.push_back("--port"); + argv.push_back("6"); + std::map<std::string, std::string> env_map; + + parser.setConfig("default.conf", "port=5"); + + 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(INIConfigFile, Comments) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + testOpts.addOptionChaining("str", "str", moe::String, "String"); + + 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=5\nstr=NotCommented"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_NOT_OK(environment.get(moe::Key("port"), &value)); + ASSERT_OK(environment.get(moe::Key("str"), &value)); + std::string str; + ASSERT_OK(value.get(&str)); + ASSERT_EQUALS(str, "NotCommented"); +} + +// Ensure switches in INI config files have the correct semantics. +// +// Switches have the following semantics: +// - Present on the command line -> set to true +// - Present in the config file -> set to value in config file +// - Present in the config file with no value (INI only) -> set to true +// - Not present -> not set to any value +TEST(INIConfigFile, Switches) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("switch1", "switch1", moe::Switch, "switch1"); + testOpts.addOptionChaining("switch2", "switch2", moe::Switch, "switch2"); + testOpts.addOptionChaining("switch3", "switch3", moe::Switch, "switch3"); + testOpts.addOptionChaining("switch4", "switch4", moe::Switch, "switch4"); + testOpts.addOptionChaining("switch5", "switch5", moe::Switch, "switch5"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("default.conf"); + argv.push_back("--switch1"); + std::map<std::string, std::string> env_map; + + parser.setConfig("default.conf", "switch2=true\nswitch3=false\nswitch5="); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + bool switch1; + ASSERT_OK(environment.get(moe::Key("switch1"), &switch1)); + ASSERT_TRUE(switch1); + bool switch2; + ASSERT_OK(environment.get(moe::Key("switch2"), &switch2)); + ASSERT_TRUE(switch2); + bool switch3; + ASSERT_OK(environment.get(moe::Key("switch3"), &switch3)); + ASSERT_FALSE(switch3); + bool switch4; + ASSERT_NOT_OK(environment.get(moe::Key("switch4"), &switch4)); + bool switch5; + ASSERT_OK(environment.get(moe::Key("switch5"), &switch5)); + ASSERT_TRUE(switch5); +} + +TEST(INIConfigFile, Monkeys) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("this", "this", moe::Switch, "This"); + testOpts.addOptionChaining("that", "that", moe::Switch, "That"); + testOpts.addOptionChaining("another", "another", moe::String, "Another"); + testOpts.addOptionChaining("other", "other", moe::String, "Other"); + + 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", + "\t this = false \n#that = true\n #another = whocares" + "\n\n other = monkeys "); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("this"), &value)); + bool thisValue; + ASSERT_OK(value.get(&thisValue)); + ASSERT_FALSE(thisValue); + ASSERT_NOT_OK(environment.get(moe::Key("that"), &value)); + ASSERT_NOT_OK(environment.get(moe::Key("another"), &value)); + ASSERT_OK(environment.get(moe::Key("other"), &value)); + std::string str; + ASSERT_OK(value.get(&str)); + ASSERT_EQUALS(str, "monkeys"); +} + +TEST(INIConfigFile, DefaultValueOverride) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port").setDefault(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(INIConfigFile, StringVector) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.ini", "multival = val1\nmultival = val2"); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("multival"), &value)); + std::vector<std::string> multival; + std::vector<std::string>::iterator multivalit; + ASSERT_OK(value.get(&multival)); + multivalit = multival.begin(); + ASSERT_EQUALS(*multivalit, "val1"); + multivalit++; + ASSERT_EQUALS(*multivalit, "val2"); +} + +TEST(INIConfigFile, StringMap) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.ini", + "multival = key1=value1\n" + "multival = key2=value2\n" + "multival = key3="); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("multival"), &value)); + std::map<std::string, std::string> multival; + std::map<std::string, std::string>::iterator multivalit; + ASSERT_OK(value.get(&multival)); + multivalit = multival.begin(); + ASSERT_EQUALS(multivalit->first, "key1"); + ASSERT_EQUALS(multivalit->second, "value1"); + multivalit++; + ASSERT_EQUALS(multivalit->first, "key2"); + ASSERT_EQUALS(multivalit->second, "value2"); + multivalit++; + ASSERT_EQUALS(multivalit->first, "key3"); + ASSERT_EQUALS(multivalit->second, ""); +} + +TEST(INIConfigFile, StringMapDuplicateKey) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.ini", + "multival = key1=value1\n" + "multival = key1=value2"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(JSONConfigFile, Basic) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + 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 : 5 }"); + + 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(JSONConfigFile, Empty) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + + 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", ""); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(JSONConfigFile, EmptyObject) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + + 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", "{}"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(JSONConfigFile, Override) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.json"); + argv.push_back("--port"); + argv.push_back("6"); + std::map<std::string, std::string> env_map; + + + parser.setConfig("config.json", "{ port : 5 }"); + + 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, UnregisteredOption) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + + 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 : 5 }"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(JSONConfigFile, DuplicateOption) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + 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 : 5, port : 5 }"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(JSONConfigFile, TypeChecking) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining( + "stringVectorVal", "stringVectorVal", moe::StringVector, "StringVectorVal"); + testOpts.addOptionChaining("boolVal", "boolVal", moe::Bool, "BoolVal"); + testOpts.addOptionChaining("doubleVal", "doubleVal", moe::Double, "DoubleVal"); + testOpts.addOptionChaining("intVal", "intVal", moe::Int, "IntVal"); + testOpts.addOptionChaining("longVal", "longVal", moe::Long, "LongVal"); + testOpts.addOptionChaining("stringVal", "stringVal", moe::String, "StringVal"); + testOpts.addOptionChaining( + "unsignedLongLongVal", "unsignedLongLongVal", moe::UnsignedLongLong, "UnsignedLongLongVal"); + testOpts.addOptionChaining("unsignedVal", "unsignedVal", moe::Unsigned, "UnsignedVal"); + testOpts.addOptionChaining("switchVal", "switchVal", moe::Switch, "SwitchVal"); + + 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; + + // Test StringVector type + std::vector<std::string> stringVectorVal; + + parser.setConfig("config.json", "{ stringVectorVal : \"scalar\" }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ stringVectorVal : \"true\" }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ stringVectorVal : \"5\" }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ stringVectorVal : [ [ \"string\" ], true, 1, 1.0 ] }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as a string vector type and treat it as an array of strings, even if the + // elements are not surrounded by quotes + environment = moe::Environment(); + parser.setConfig("config.json", "{ stringVectorVal : [ \"string\", bare, true, 1, 1.0 ] }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("stringVectorVal"), &value)); + std::vector<std::string>::iterator stringVectorValIt; + ASSERT_OK(value.get(&stringVectorVal)); + stringVectorValIt = stringVectorVal.begin(); + ASSERT_EQUALS(*stringVectorValIt, "string"); + stringVectorValIt++; + ASSERT_EQUALS(*stringVectorValIt, "bare"); + stringVectorValIt++; + ASSERT_EQUALS(*stringVectorValIt, "true"); + stringVectorValIt++; + ASSERT_EQUALS(*stringVectorValIt, "1"); + stringVectorValIt++; + ASSERT_EQUALS(*stringVectorValIt, "1.0"); + + // Test Bool type + bool boolVal; + environment = moe::Environment(); + parser.setConfig("config.json", "{ boolVal : \"lies\" }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ boolVal : truth }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ boolVal : 1 }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as a bool type and try to convert it to a bool, even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "{ boolVal : \"true\" }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("boolVal"), &value)); + ASSERT_OK(value.get(&boolVal)); + ASSERT_EQUALS(boolVal, true); + environment = moe::Environment(); + parser.setConfig("config.json", "{ boolVal : false }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("boolVal"), &value)); + ASSERT_OK(value.get(&boolVal)); + ASSERT_EQUALS(boolVal, false); + + // Test Double type + double doubleVal; + environment = moe::Environment(); + parser.setConfig("config.json", "{ doubleVal : \"double the monkeys\" }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ doubleVal : true }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as a double type and try to convert it to a double, even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "{ doubleVal : 1.5 }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, 1.5); + environment = moe::Environment(); + parser.setConfig("config.json", "{ doubleVal : -1.5 }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, -1.5); + environment = moe::Environment(); + parser.setConfig("config.json", "{ doubleVal : \"3.14\" }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, 3.14); + environment = moe::Environment(); + parser.setConfig("config.json", "{ doubleVal : \"-3.14\" }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, -3.14); + + // Test Int type + int intVal; + environment = moe::Environment(); + parser.setConfig("config.json", "{ intVal : \"hungry hippos\" }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ intVal : 1.5 }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ intVal : 18446744073709551617 }"); // 2^64 + 1 + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ intVal : true }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as an int type and try to convert it to a int, even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "{ intVal : \"5\" }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("intVal"), &value)); + ASSERT_OK(value.get(&intVal)); + ASSERT_EQUALS(intVal, 5); + + environment = moe::Environment(); + parser.setConfig("config.json", "{ intVal : \"-5\" }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("intVal"), &value)); + ASSERT_OK(value.get(&intVal)); + ASSERT_EQUALS(intVal, -5); + + // Test Long type + long longVal; + environment = moe::Environment(); + parser.setConfig("config.json", "{ longVal : \"in an eating race\" }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ longVal : 1.5 }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ longVal : 18446744073709551617 }"); // 2^64 + 1 + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ longVal : true }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as a long type and try to convert it to a long, even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "{ longVal : \"5\" }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("longVal"), &value)); + ASSERT_OK(value.get(&longVal)); + ASSERT_EQUALS(longVal, 5); + + environment = moe::Environment(); + parser.setConfig("config.json", "{ longVal : \"-5\" }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("longVal"), &value)); + ASSERT_OK(value.get(&longVal)); + ASSERT_EQUALS(longVal, -5); + + // Test String type + std::string stringVal; + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as a string type and treat it as a string, even if the element is not + // surrounded by quotes + environment = moe::Environment(); + parser.setConfig("config.json", "{ stringVal : }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); + ASSERT_OK(value.get(&stringVal)); + ASSERT_EQUALS(stringVal, ""); + + environment = moe::Environment(); + parser.setConfig("config.json", "{ stringVal : \"1000\" }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); + ASSERT_OK(value.get(&stringVal)); + ASSERT_EQUALS(stringVal, "1000"); + + environment = moe::Environment(); + parser.setConfig("config.json", "{ stringVal : wat man }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); + ASSERT_OK(value.get(&stringVal)); + ASSERT_EQUALS(stringVal, "wat man"); + + environment = moe::Environment(); + parser.setConfig("config.json", "{ stringVal : true 1 string 1.0 }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); + ASSERT_OK(value.get(&stringVal)); + ASSERT_EQUALS(stringVal, "true 1 string 1.0"); + + // Test UnsignedLongLong type + unsigned long long unsignedLongLongVal; + environment = moe::Environment(); + parser.setConfig("config.json", "{ unsignedLongLongVal : \"unsigned hungry hippos\" }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ unsignedLongLongVal : 1.5 }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ unsignedLongLongVal : 18446744073709551617 }"); // 2^64 + 1 + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ unsignedLongLongVal : true }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ unsignedLongLongVal : \"-5\" }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as an unsigned long long type and try to convert it to an unsigned long long, + // even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "{ unsignedLongLongVal : \"5\" }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); + ASSERT_OK(value.get(&unsignedLongLongVal)); + ASSERT_EQUALS(unsignedLongLongVal, 5ULL); + + // Test Unsigned type + unsigned unsignedVal; + environment = moe::Environment(); + parser.setConfig("config.json", "{ unsignedVal : \"unsigned hungry hippos\" }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ unsignedVal : 1.5 }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ unsignedVal : 18446744073709551617 }"); // 2^64 + 1 + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ unsignedVal : true }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ unsignedVal : \"-5\" }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as an unsigned type and try to convert it to an unsigned, even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "{ unsignedVal : \"5\" }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); + ASSERT_OK(value.get(&unsignedVal)); + ASSERT_EQUALS(unsignedVal, 5U); + + // Test Switch type + bool switchVal; + environment = moe::Environment(); + parser.setConfig("config.json", "{ switchVal : \"lies\" }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ switchVal : truth }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "{ switchVal : 1 }"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as a switch type and try to convert it to a bool, even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "{ switchVal : \"true\" }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("switchVal"), &value)); + ASSERT_OK(value.get(&switchVal)); + ASSERT_EQUALS(switchVal, true); + environment = moe::Environment(); + parser.setConfig("config.json", "{ switchVal : false }"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("switchVal"), &switchVal)); + ASSERT_FALSE(switchVal); +} + +TEST(JSONConfigFile, Nested) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("nested.port", "port", moe::Int, "Port"); + + 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", "{ nested : { port : 5 } }"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("nested.port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); +} + +TEST(JSONConfigFile, Dotted) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("dotted.port", "port", moe::Int, "Port"); + + 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", "{ \"dotted.port\" : 5 }"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("dotted.port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); +} + +TEST(JSONConfigFile, DottedAndNested) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("dottednested.var1", "var1", moe::Int, "Var1"); + testOpts.addOptionChaining("dottednested.var2", "var2", moe::Int, "Var2"); + + 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", "{ \"dottednested.var1\" : 5, dottednested : { var2 : 6 } }"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("dottednested.var1"), &value)); + int var1; + ASSERT_OK(value.get(&var1)); + ASSERT_EQUALS(var1, 5); + ASSERT_OK(environment.get(moe::Key("dottednested.var2"), &value)); + int var2; + ASSERT_OK(value.get(&var2)); + ASSERT_EQUALS(var2, 6); +} + +TEST(JSONConfigFile, StringVector) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); + + 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", "{ multival : [ \"val1\", \"val2\" ] }"); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("multival"), &value)); + std::vector<std::string> multival; + std::vector<std::string>::iterator multivalit; + ASSERT_OK(value.get(&multival)); + multivalit = multival.begin(); + ASSERT_EQUALS(*multivalit, "val1"); + multivalit++; + ASSERT_EQUALS(*multivalit, "val2"); +} + +TEST(JSONConfigFile, StringMap) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); + + 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", + "{ multival : { key1 : \"value1\", key2 : \"value2\", key3 : \"\" } }"); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("multival"), &value)); + std::map<std::string, std::string> multival; + std::map<std::string, std::string>::iterator multivalit; + ASSERT_OK(value.get(&multival)); + multivalit = multival.begin(); + ASSERT_EQUALS(multivalit->first, "key1"); + ASSERT_EQUALS(multivalit->second, "value1"); + multivalit++; + ASSERT_EQUALS(multivalit->first, "key2"); + ASSERT_EQUALS(multivalit->second, "value2"); + multivalit++; + ASSERT_EQUALS(multivalit->first, "key3"); + ASSERT_EQUALS(multivalit->second, ""); +} + +TEST(JSONConfigFile, StringMapDuplicateKey) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); + + 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", "{ multival : { key1 : \"value1\", key1 : \"value2\" } }"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(JSONConfigFile, StringVectorNonString) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); + + 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; + + // NOTE: The yaml config file just reads things as strings, and it's up to us to decide what + // the type should be later. This means that we can't tell the difference between when a + // user provides a non string value or a string value in some cases. + parser.setConfig("config.json", "{ multival : [ 1, true ] }"); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("multival"), &value)); + std::vector<std::string> multival; + std::vector<std::string>::iterator multivalit; + ASSERT_OK(value.get(&multival)); + multivalit = multival.begin(); + ASSERT_EQUALS(*multivalit, "1"); + multivalit++; + ASSERT_EQUALS(*multivalit, "true"); +} + +TEST(JSONConfigFile, DefaultValueOverride) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port").setDefault(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; + + moe::OptionSection testOpts; + + // TODO: Should the error be in here? + testOpts.addOptionChaining("config", "config", moe::Int, "Config file to parse"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("1"); + std::map<std::string, std::string> env_map; + + parser.setConfig("default.conf", ""); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(ConfigFromFilesystem, JSONGood) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back(TEST_CONFIG_PATH("good.json")); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); +} + +TEST(ConfigFromFilesystem, INIGood) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back(TEST_CONFIG_PATH("good.conf")); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); +} + +TEST(ConfigFromFilesystem, Empty) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back(TEST_CONFIG_PATH("empty.json")); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(JSONConfigFile, ComposingStringVector) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("setParameter", "setParameter", moe::StringVector, "Multiple Values") + .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, "val3"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val4"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val1"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val2"); +} + +TEST(JSONConfigFile, ComposingStringMap) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("setParameter", "setParameter", moe::StringMap, "Multiple Values") + .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("key1=value1"); + argv.push_back("--setParameter"); + argv.push_back("key2=value2"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.json", + "{ setParameter : { key2 : \"overridden_value2\", key3 : \"value3\" } }"); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("setParameter"), &value)); + std::map<std::string, std::string> setParameter; + std::map<std::string, std::string>::iterator setParameterIt; + ASSERT_OK(value.get(&setParameter)); + ASSERT_EQUALS(setParameter.size(), static_cast<size_t>(3)); + setParameterIt = setParameter.begin(); + ASSERT_EQUALS(setParameterIt->first, "key1"); + ASSERT_EQUALS(setParameterIt->second, "value1"); + setParameterIt++; + ASSERT_EQUALS(setParameterIt->first, "key2"); + ASSERT_EQUALS(setParameterIt->second, "value2"); + setParameterIt++; + ASSERT_EQUALS(setParameterIt->first, "key3"); + ASSERT_EQUALS(setParameterIt->second, "value3"); +} + +TEST(INIConfigFile, ComposingStringVector) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("setParameter", "setParameter", moe::StringVector, "Multiple Values") + .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, "val3"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val4"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val1"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val2"); +} + +TEST(INIConfigFile, ComposingStringMap) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("setParameter", "setParameter", moe::StringMap, "Multiple Values") + .composing(); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); + argv.push_back("--setParameter"); + argv.push_back("key1=value1"); + argv.push_back("--setParameter"); + argv.push_back("key2=value2"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.ini", "setParameter=key2=overridden_value2\nsetParameter=key3=value3"); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("setParameter"), &value)); + std::map<std::string, std::string> setParameter; + std::map<std::string, std::string>::iterator setParameterIt; + ASSERT_OK(value.get(&setParameter)); + ASSERT_EQUALS(setParameter.size(), static_cast<size_t>(3)); + setParameterIt = setParameter.begin(); + ASSERT_EQUALS(setParameterIt->first, "key1"); + ASSERT_EQUALS(setParameterIt->second, "value1"); + setParameterIt++; + ASSERT_EQUALS(setParameterIt->first, "key2"); + ASSERT_EQUALS(setParameterIt->second, "value2"); + setParameterIt++; + ASSERT_EQUALS(setParameterIt->first, "key3"); + ASSERT_EQUALS(setParameterIt->second, "value3"); +} + +TEST(YAMLConfigFile, ComposingStringVector) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("setParameter", "setParameter", moe::StringVector, "Multiple Values") + .composing(); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + 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.yaml", "setParameter : \n - \"val3\"\n - \"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, "val3"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val4"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val1"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val2"); +} + +TEST(YAMLConfigFile, ComposingStringMap) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("setParameter", "setParameter", moe::StringMap, "Multiple Values") + .composing(); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + argv.push_back("--setParameter"); + argv.push_back("key1=value1"); + argv.push_back("--setParameter"); + argv.push_back("key2=value2"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", + // NOTE: Indentation is used to determine whether an option is in a sub + // category, so the spaces after the newlines before key2 and key3 is + // significant + "setParameter:\n key2: \"overridden_value2\"\n key3: \"value3\""); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("setParameter"), &value)); + std::map<std::string, std::string> setParameter; + std::map<std::string, std::string>::iterator setParameterIt; + ASSERT_OK(value.get(&setParameter)); + ASSERT_EQUALS(setParameter.size(), static_cast<size_t>(3)); + setParameterIt = setParameter.begin(); + ASSERT_EQUALS(setParameterIt->first, "key1"); + ASSERT_EQUALS(setParameterIt->second, "value1"); + setParameterIt++; + ASSERT_EQUALS(setParameterIt->first, "key2"); + ASSERT_EQUALS(setParameterIt->second, "value2"); + setParameterIt++; + ASSERT_EQUALS(setParameterIt->first, "key3"); + ASSERT_EQUALS(setParameterIt->second, "value3"); +} + +TEST(LegacyInterface, Good) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + 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)); + ASSERT_TRUE(environment.count("port")); + try { int port; - ASSERT_OK(value.get(&port)); + port = environment["port"].as<int>(); ASSERT_EQUALS(port, 5); - } - - TEST(INIConfigFile, Empty) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - - 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", ""); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(INIConfigFile, Override) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("default.conf"); - argv.push_back("--port"); - argv.push_back("6"); - std::map<std::string, std::string> env_map; - - parser.setConfig("default.conf", "port=5"); - - 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(INIConfigFile, Comments) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - testOpts.addOptionChaining("str", "str", moe::String, "String"); - - 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=5\nstr=NotCommented"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - moe::Value value; - ASSERT_NOT_OK(environment.get(moe::Key("port"), &value)); - ASSERT_OK(environment.get(moe::Key("str"), &value)); - std::string str; - ASSERT_OK(value.get(&str)); - ASSERT_EQUALS(str, "NotCommented"); - } - - // Ensure switches in INI config files have the correct semantics. - // - // Switches have the following semantics: - // - Present on the command line -> set to true - // - Present in the config file -> set to value in config file - // - Present in the config file with no value (INI only) -> set to true - // - Not present -> not set to any value - TEST(INIConfigFile, Switches) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("switch1", "switch1", moe::Switch, "switch1"); - testOpts.addOptionChaining("switch2", "switch2", moe::Switch, "switch2"); - testOpts.addOptionChaining("switch3", "switch3", moe::Switch, "switch3"); - testOpts.addOptionChaining("switch4", "switch4", moe::Switch, "switch4"); - testOpts.addOptionChaining("switch5", "switch5", moe::Switch, "switch5"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("default.conf"); - argv.push_back("--switch1"); - std::map<std::string, std::string> env_map; - - parser.setConfig("default.conf", "switch2=true\nswitch3=false\nswitch5="); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - bool switch1; - ASSERT_OK(environment.get(moe::Key("switch1"), &switch1)); - ASSERT_TRUE(switch1); - bool switch2; - ASSERT_OK(environment.get(moe::Key("switch2"), &switch2)); - ASSERT_TRUE(switch2); - bool switch3; - ASSERT_OK(environment.get(moe::Key("switch3"), &switch3)); - ASSERT_FALSE(switch3); - bool switch4; - ASSERT_NOT_OK(environment.get(moe::Key("switch4"), &switch4)); - bool switch5; - ASSERT_OK(environment.get(moe::Key("switch5"), &switch5)); - ASSERT_TRUE(switch5); - } - - TEST(INIConfigFile, Monkeys) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("this", "this", moe::Switch, "This"); - testOpts.addOptionChaining("that", "that", moe::Switch, "That"); - testOpts.addOptionChaining("another", "another", moe::String, "Another"); - testOpts.addOptionChaining("other", "other", moe::String, "Other"); - - 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", - "\t this = false \n#that = true\n #another = whocares" - "\n\n other = monkeys "); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - moe::Value value; - ASSERT_OK(environment.get(moe::Key("this"), &value)); - bool thisValue; - ASSERT_OK(value.get(&thisValue)); - ASSERT_FALSE(thisValue); - ASSERT_NOT_OK(environment.get(moe::Key("that"), &value)); - ASSERT_NOT_OK(environment.get(moe::Key("another"), &value)); - ASSERT_OK(environment.get(moe::Key("other"), &value)); - std::string str; - ASSERT_OK(value.get(&str)); - ASSERT_EQUALS(str, "monkeys"); - } - - TEST(INIConfigFile, DefaultValueOverride) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port").setDefault(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(INIConfigFile, StringVector) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.ini", "multival = val1\nmultival = val2"); - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("multival"), &value)); - std::vector<std::string> multival; - std::vector<std::string>::iterator multivalit; - ASSERT_OK(value.get(&multival)); - multivalit = multival.begin(); - ASSERT_EQUALS(*multivalit, "val1"); - multivalit++; - ASSERT_EQUALS(*multivalit, "val2"); - } - - TEST(INIConfigFile, StringMap) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.ini", - "multival = key1=value1\n" - "multival = key2=value2\n" - "multival = key3="); - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("multival"), &value)); - std::map<std::string, std::string> multival; - std::map<std::string, std::string>::iterator multivalit; - ASSERT_OK(value.get(&multival)); - multivalit = multival.begin(); - ASSERT_EQUALS(multivalit->first, "key1"); - ASSERT_EQUALS(multivalit->second, "value1"); - multivalit++; - ASSERT_EQUALS(multivalit->first, "key2"); - ASSERT_EQUALS(multivalit->second, "value2"); - multivalit++; - ASSERT_EQUALS(multivalit->first, "key3"); - ASSERT_EQUALS(multivalit->second, ""); - } - - TEST(INIConfigFile, StringMapDuplicateKey) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.ini", - "multival = key1=value1\n" - "multival = key1=value2"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(JSONConfigFile, Basic) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - 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 : 5 }"); - - 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(JSONConfigFile, Empty) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - - 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", ""); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(JSONConfigFile, EmptyObject) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - - 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", "{}"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(JSONConfigFile, Override) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.json"); - argv.push_back("--port"); - argv.push_back("6"); - std::map<std::string, std::string> env_map; - - - parser.setConfig("config.json", "{ port : 5 }"); - - 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, UnregisteredOption) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - - 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 : 5 }"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(JSONConfigFile, DuplicateOption) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - 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 : 5, port : 5 }"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(JSONConfigFile, TypeChecking) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("stringVectorVal", "stringVectorVal", moe::StringVector, - "StringVectorVal"); - testOpts.addOptionChaining("boolVal", "boolVal", moe::Bool, "BoolVal"); - testOpts.addOptionChaining("doubleVal", "doubleVal", moe::Double, "DoubleVal"); - testOpts.addOptionChaining("intVal", "intVal", moe::Int, "IntVal"); - testOpts.addOptionChaining("longVal", "longVal", moe::Long, "LongVal"); - testOpts.addOptionChaining("stringVal", "stringVal", moe::String, "StringVal"); - testOpts.addOptionChaining("unsignedLongLongVal", "unsignedLongLongVal", - moe::UnsignedLongLong, "UnsignedLongLongVal"); - testOpts.addOptionChaining("unsignedVal", "unsignedVal", moe::Unsigned, "UnsignedVal"); - testOpts.addOptionChaining("switchVal", "switchVal", moe::Switch, "SwitchVal"); - - 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; - - // Test StringVector type - std::vector<std::string> stringVectorVal; - - parser.setConfig("config.json", "{ stringVectorVal : \"scalar\" }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ stringVectorVal : \"true\" }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ stringVectorVal : \"5\" }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ stringVectorVal : [ [ \"string\" ], true, 1, 1.0 ] }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as a string vector type and treat it as an array of strings, even if the - // elements are not surrounded by quotes - environment = moe::Environment(); - parser.setConfig("config.json", "{ stringVectorVal : [ \"string\", bare, true, 1, 1.0 ] }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("stringVectorVal"), &value)); - std::vector<std::string>::iterator stringVectorValIt; - ASSERT_OK(value.get(&stringVectorVal)); - stringVectorValIt = stringVectorVal.begin(); - ASSERT_EQUALS(*stringVectorValIt, "string"); - stringVectorValIt++; - ASSERT_EQUALS(*stringVectorValIt, "bare"); - stringVectorValIt++; - ASSERT_EQUALS(*stringVectorValIt, "true"); - stringVectorValIt++; - ASSERT_EQUALS(*stringVectorValIt, "1"); - stringVectorValIt++; - ASSERT_EQUALS(*stringVectorValIt, "1.0"); - - // Test Bool type - bool boolVal; - environment = moe::Environment(); - parser.setConfig("config.json", "{ boolVal : \"lies\" }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ boolVal : truth }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ boolVal : 1 }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as a bool type and try to convert it to a bool, even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "{ boolVal : \"true\" }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("boolVal"), &value)); - ASSERT_OK(value.get(&boolVal)); - ASSERT_EQUALS(boolVal, true); - environment = moe::Environment(); - parser.setConfig("config.json", "{ boolVal : false }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("boolVal"), &value)); - ASSERT_OK(value.get(&boolVal)); - ASSERT_EQUALS(boolVal, false); - - // Test Double type - double doubleVal; - environment = moe::Environment(); - parser.setConfig("config.json", "{ doubleVal : \"double the monkeys\" }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ doubleVal : true }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as a double type and try to convert it to a double, even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "{ doubleVal : 1.5 }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, 1.5); - environment = moe::Environment(); - parser.setConfig("config.json", "{ doubleVal : -1.5 }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, -1.5); - environment = moe::Environment(); - parser.setConfig("config.json", "{ doubleVal : \"3.14\" }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, 3.14); - environment = moe::Environment(); - parser.setConfig("config.json", "{ doubleVal : \"-3.14\" }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, -3.14); - - // Test Int type - int intVal; - environment = moe::Environment(); - parser.setConfig("config.json", "{ intVal : \"hungry hippos\" }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ intVal : 1.5 }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ intVal : 18446744073709551617 }"); // 2^64 + 1 - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ intVal : true }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as an int type and try to convert it to a int, even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "{ intVal : \"5\" }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("intVal"), &value)); - ASSERT_OK(value.get(&intVal)); - ASSERT_EQUALS(intVal, 5); - - environment = moe::Environment(); - parser.setConfig("config.json", "{ intVal : \"-5\" }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("intVal"), &value)); - ASSERT_OK(value.get(&intVal)); - ASSERT_EQUALS(intVal, -5); - - // Test Long type - long longVal; - environment = moe::Environment(); - parser.setConfig("config.json", "{ longVal : \"in an eating race\" }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ longVal : 1.5 }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ longVal : 18446744073709551617 }"); // 2^64 + 1 - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ longVal : true }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as a long type and try to convert it to a long, even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "{ longVal : \"5\" }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("longVal"), &value)); - ASSERT_OK(value.get(&longVal)); - ASSERT_EQUALS(longVal, 5); - - environment = moe::Environment(); - parser.setConfig("config.json", "{ longVal : \"-5\" }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("longVal"), &value)); - ASSERT_OK(value.get(&longVal)); - ASSERT_EQUALS(longVal, -5); - - // Test String type - std::string stringVal; - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as a string type and treat it as a string, even if the element is not - // surrounded by quotes - environment = moe::Environment(); - parser.setConfig("config.json", "{ stringVal : }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); - ASSERT_OK(value.get(&stringVal)); - ASSERT_EQUALS(stringVal, ""); - - environment = moe::Environment(); - parser.setConfig("config.json", "{ stringVal : \"1000\" }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); - ASSERT_OK(value.get(&stringVal)); - ASSERT_EQUALS(stringVal, "1000"); - - environment = moe::Environment(); - parser.setConfig("config.json", "{ stringVal : wat man }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); - ASSERT_OK(value.get(&stringVal)); - ASSERT_EQUALS(stringVal, "wat man"); - - environment = moe::Environment(); - parser.setConfig("config.json", "{ stringVal : true 1 string 1.0 }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); - ASSERT_OK(value.get(&stringVal)); - ASSERT_EQUALS(stringVal, "true 1 string 1.0"); - - // Test UnsignedLongLong type - unsigned long long unsignedLongLongVal; - environment = moe::Environment(); - parser.setConfig("config.json", "{ unsignedLongLongVal : \"unsigned hungry hippos\" }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ unsignedLongLongVal : 1.5 }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", - "{ unsignedLongLongVal : 18446744073709551617 }"); // 2^64 + 1 - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ unsignedLongLongVal : true }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ unsignedLongLongVal : \"-5\" }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as an unsigned long long type and try to convert it to an unsigned long long, - // even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "{ unsignedLongLongVal : \"5\" }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); - ASSERT_OK(value.get(&unsignedLongLongVal)); - ASSERT_EQUALS(unsignedLongLongVal, 5ULL); - - // Test Unsigned type - unsigned unsignedVal; - environment = moe::Environment(); - parser.setConfig("config.json", "{ unsignedVal : \"unsigned hungry hippos\" }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ unsignedVal : 1.5 }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ unsignedVal : 18446744073709551617 }"); // 2^64 + 1 - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ unsignedVal : true }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ unsignedVal : \"-5\" }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as an unsigned type and try to convert it to an unsigned, even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "{ unsignedVal : \"5\" }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); - ASSERT_OK(value.get(&unsignedVal)); - ASSERT_EQUALS(unsignedVal, 5U); - - // Test Switch type - bool switchVal; - environment = moe::Environment(); - parser.setConfig("config.json", "{ switchVal : \"lies\" }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ switchVal : truth }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "{ switchVal : 1 }"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as a switch type and try to convert it to a bool, even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "{ switchVal : \"true\" }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("switchVal"), &value)); - ASSERT_OK(value.get(&switchVal)); - ASSERT_EQUALS(switchVal, true); - environment = moe::Environment(); - parser.setConfig("config.json", "{ switchVal : false }"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("switchVal"), &switchVal)); - ASSERT_FALSE(switchVal); - } - - TEST(JSONConfigFile, Nested) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("nested.port", "port", moe::Int, "Port"); - - 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", "{ nested : { port : 5 } }"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - moe::Value value; - ASSERT_OK(environment.get(moe::Key("nested.port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - } - - TEST(JSONConfigFile, Dotted) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("dotted.port", "port", moe::Int, "Port"); - - 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", "{ \"dotted.port\" : 5 }"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - moe::Value value; - ASSERT_OK(environment.get(moe::Key("dotted.port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - } - - TEST(JSONConfigFile, DottedAndNested) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("dottednested.var1", "var1", moe::Int, "Var1"); - testOpts.addOptionChaining("dottednested.var2", "var2", moe::Int, "Var2"); - - 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", - "{ \"dottednested.var1\" : 5, dottednested : { var2 : 6 } }"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - moe::Value value; - ASSERT_OK(environment.get(moe::Key("dottednested.var1"), &value)); - int var1; - ASSERT_OK(value.get(&var1)); - ASSERT_EQUALS(var1, 5); - ASSERT_OK(environment.get(moe::Key("dottednested.var2"), &value)); - int var2; - ASSERT_OK(value.get(&var2)); - ASSERT_EQUALS(var2, 6); - } - - TEST(JSONConfigFile, StringVector) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); - - 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", "{ multival : [ \"val1\", \"val2\" ] }"); - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("multival"), &value)); - std::vector<std::string> multival; - std::vector<std::string>::iterator multivalit; - ASSERT_OK(value.get(&multival)); - multivalit = multival.begin(); - ASSERT_EQUALS(*multivalit, "val1"); - multivalit++; - ASSERT_EQUALS(*multivalit, "val2"); - } - - TEST(JSONConfigFile, StringMap) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); - - 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", - "{ multival : { key1 : \"value1\", key2 : \"value2\", key3 : \"\" } }"); - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("multival"), &value)); - std::map<std::string, std::string> multival; - std::map<std::string, std::string>::iterator multivalit; - ASSERT_OK(value.get(&multival)); - multivalit = multival.begin(); - ASSERT_EQUALS(multivalit->first, "key1"); - ASSERT_EQUALS(multivalit->second, "value1"); - multivalit++; - ASSERT_EQUALS(multivalit->first, "key2"); - ASSERT_EQUALS(multivalit->second, "value2"); - multivalit++; - ASSERT_EQUALS(multivalit->first, "key3"); - ASSERT_EQUALS(multivalit->second, ""); - } - - TEST(JSONConfigFile, StringMapDuplicateKey) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); - - 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", - "{ multival : { key1 : \"value1\", key1 : \"value2\" } }"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(JSONConfigFile, StringVectorNonString) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); - - 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; - - // NOTE: The yaml config file just reads things as strings, and it's up to us to decide what - // the type should be later. This means that we can't tell the difference between when a - // user provides a non string value or a string value in some cases. - parser.setConfig("config.json", "{ multival : [ 1, true ] }"); - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("multival"), &value)); - std::vector<std::string> multival; - std::vector<std::string>::iterator multivalit; - ASSERT_OK(value.get(&multival)); - multivalit = multival.begin(); - ASSERT_EQUALS(*multivalit, "1"); - multivalit++; - ASSERT_EQUALS(*multivalit, "true"); - } - - TEST(JSONConfigFile, DefaultValueOverride) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port").setDefault(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; - - moe::OptionSection testOpts; - - // TODO: Should the error be in here? - testOpts.addOptionChaining("config", "config", moe::Int, "Config file to parse"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("1"); - std::map<std::string, std::string> env_map; - - parser.setConfig("default.conf", ""); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(ConfigFromFilesystem, JSONGood) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back(TEST_CONFIG_PATH("good.json")); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - } - - TEST(ConfigFromFilesystem, INIGood) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back(TEST_CONFIG_PATH("good.conf")); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - } - - TEST(ConfigFromFilesystem, Empty) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back(TEST_CONFIG_PATH("empty.json")); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(JSONConfigFile, ComposingStringVector) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("setParameter", "setParameter", moe::StringVector, - "Multiple Values").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, "val3"); - setParameterit++; - ASSERT_EQUALS(*setParameterit, "val4"); - setParameterit++; - ASSERT_EQUALS(*setParameterit, "val1"); - setParameterit++; - ASSERT_EQUALS(*setParameterit, "val2"); - } - - TEST(JSONConfigFile, ComposingStringMap) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("setParameter", "setParameter", moe::StringMap, - "Multiple Values").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("key1=value1"); - argv.push_back("--setParameter"); - argv.push_back("key2=value2"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.json", - "{ setParameter : { key2 : \"overridden_value2\", key3 : \"value3\" } }"); - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("setParameter"), &value)); - std::map<std::string, std::string> setParameter; - std::map<std::string, std::string>::iterator setParameterIt; - ASSERT_OK(value.get(&setParameter)); - ASSERT_EQUALS(setParameter.size(), static_cast<size_t>(3)); - setParameterIt = setParameter.begin(); - ASSERT_EQUALS(setParameterIt->first, "key1"); - ASSERT_EQUALS(setParameterIt->second, "value1"); - setParameterIt++; - ASSERT_EQUALS(setParameterIt->first, "key2"); - ASSERT_EQUALS(setParameterIt->second, "value2"); - setParameterIt++; - ASSERT_EQUALS(setParameterIt->first, "key3"); - ASSERT_EQUALS(setParameterIt->second, "value3"); - } - - TEST(INIConfigFile, ComposingStringVector) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("setParameter", "setParameter", moe::StringVector, - "Multiple Values").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, "val3"); - setParameterit++; - ASSERT_EQUALS(*setParameterit, "val4"); - setParameterit++; - ASSERT_EQUALS(*setParameterit, "val1"); - setParameterit++; - ASSERT_EQUALS(*setParameterit, "val2"); - } - - TEST(INIConfigFile, ComposingStringMap) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("setParameter", "setParameter", moe::StringMap, - "Multiple Values").composing(); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); - argv.push_back("--setParameter"); - argv.push_back("key1=value1"); - argv.push_back("--setParameter"); - argv.push_back("key2=value2"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.ini", - "setParameter=key2=overridden_value2\nsetParameter=key3=value3"); - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("setParameter"), &value)); - std::map<std::string, std::string> setParameter; - std::map<std::string, std::string>::iterator setParameterIt; - ASSERT_OK(value.get(&setParameter)); - ASSERT_EQUALS(setParameter.size(), static_cast<size_t>(3)); - setParameterIt = setParameter.begin(); - ASSERT_EQUALS(setParameterIt->first, "key1"); - ASSERT_EQUALS(setParameterIt->second, "value1"); - setParameterIt++; - ASSERT_EQUALS(setParameterIt->first, "key2"); - ASSERT_EQUALS(setParameterIt->second, "value2"); - setParameterIt++; - ASSERT_EQUALS(setParameterIt->first, "key3"); - ASSERT_EQUALS(setParameterIt->second, "value3"); - } - - TEST(YAMLConfigFile, ComposingStringVector) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("setParameter", "setParameter", moe::StringVector, - "Multiple Values").composing(); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - 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.yaml", "setParameter : \n - \"val3\"\n - \"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, "val3"); - setParameterit++; - ASSERT_EQUALS(*setParameterit, "val4"); - setParameterit++; - ASSERT_EQUALS(*setParameterit, "val1"); - setParameterit++; - ASSERT_EQUALS(*setParameterit, "val2"); - } - - TEST(YAMLConfigFile, ComposingStringMap) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("setParameter", "setParameter", moe::StringMap, - "Multiple Values").composing(); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - argv.push_back("--setParameter"); - argv.push_back("key1=value1"); - argv.push_back("--setParameter"); - argv.push_back("key2=value2"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.yaml", - // NOTE: Indentation is used to determine whether an option is in a sub - // category, so the spaces after the newlines before key2 and key3 is - // significant - "setParameter:\n key2: \"overridden_value2\"\n key3: \"value3\""); - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("setParameter"), &value)); - std::map<std::string, std::string> setParameter; - std::map<std::string, std::string>::iterator setParameterIt; - ASSERT_OK(value.get(&setParameter)); - ASSERT_EQUALS(setParameter.size(), static_cast<size_t>(3)); - setParameterIt = setParameter.begin(); - ASSERT_EQUALS(setParameterIt->first, "key1"); - ASSERT_EQUALS(setParameterIt->second, "value1"); - setParameterIt++; - ASSERT_EQUALS(setParameterIt->first, "key2"); - ASSERT_EQUALS(setParameterIt->second, "value2"); - setParameterIt++; - ASSERT_EQUALS(setParameterIt->first, "key3"); - ASSERT_EQUALS(setParameterIt->second, "value3"); - } - - TEST(LegacyInterface, Good) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - 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)); - ASSERT_TRUE(environment.count("port")); - try { - int port; - port = environment["port"].as<int>(); - ASSERT_EQUALS(port, 5); - } - catch ( std::exception &e ) { - FAIL(e.what()); + } catch (std::exception& e) { + FAIL(e.what()); + } +} + +TEST(LegacyInterface, NotSpecified) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + 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)); + ASSERT_FALSE(environment.count("port")); +} + +TEST(LegacyInterface, BadType) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + 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)); + ASSERT_TRUE(environment.count("port")); + std::string port; + try { + port = environment["port"].as<std::string>(); + FAIL("Expected exception trying to convert int to type string"); + } catch (std::exception& e) { + } +} + +TEST(ChainingInterface, GoodReference) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + // This test is to make sure our reference stays good even after we add more options. This + // would not be true if we were using a std::vector in our option section which may need to + // be moved and resized. + moe::OptionDescription& optionRef = + testOpts.addOptionChaining("ref", "ref", moe::String, "Save this Reference"); + int i; + for (i = 0; i < 100; i++) { + ::mongo::StringBuilder sb; + sb << "filler" << i; + testOpts.addOptionChaining(sb.str(), sb.str(), moe::String, "Filler Option"); + } + moe::Value defaultVal(std::string("default")); + moe::Value implicitVal(std::string("implicit")); + optionRef.hidden().setDefault(defaultVal); + optionRef.setImplicit(implicitVal); + + std::vector<moe::OptionDescription> options_vector; + ASSERT_OK(testOpts.getAllOptions(&options_vector)); + + bool foundRef = false; + for (std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); + iterator++) { + if (iterator->_dottedName == "ref") { + ASSERT_EQUALS(iterator->_singleName, "ref"); + ASSERT_EQUALS(iterator->_type, moe::String); + ASSERT_EQUALS(iterator->_description, "Save this Reference"); + ASSERT_EQUALS(iterator->_isVisible, false); + ASSERT_TRUE(iterator->_default.equal(defaultVal)); + ASSERT_TRUE(iterator->_implicit.equal(implicitVal)); + ASSERT_EQUALS(iterator->_isComposing, false); + foundRef = true; } } - - TEST(LegacyInterface, NotSpecified) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - 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)); - ASSERT_FALSE(environment.count("port")); - } - - TEST(LegacyInterface, BadType) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - 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)); - ASSERT_TRUE(environment.count("port")); - std::string port; - try { - port = environment["port"].as<std::string>(); - FAIL("Expected exception trying to convert int to type string"); - } - catch ( std::exception &e ) { - } - } - - TEST(ChainingInterface, GoodReference) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - // This test is to make sure our reference stays good even after we add more options. This - // would not be true if we were using a std::vector in our option section which may need to - // be moved and resized. - moe::OptionDescription& optionRef = testOpts.addOptionChaining("ref", "ref", moe::String, - "Save this Reference"); - int i; - for (i = 0; i < 100; i++) { + if (!foundRef) { + FAIL("Could not find \"ref\" options that we registered"); + } +} + +TEST(ChainingInterface, Basic) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("basic", "basic", moe::String, "Default Option"); + + std::vector<moe::OptionDescription> options_vector; + ASSERT_OK(testOpts.getAllOptions(&options_vector)); + + for (std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); + iterator++) { + if (iterator->_dottedName == "basic") { + ASSERT_EQUALS(iterator->_singleName, "basic"); + ASSERT_EQUALS(iterator->_type, moe::String); + ASSERT_EQUALS(iterator->_description, "Default Option"); + ASSERT_EQUALS(iterator->_isVisible, true); + ASSERT_TRUE(iterator->_default.isEmpty()); + ASSERT_TRUE(iterator->_implicit.isEmpty()); + ASSERT_EQUALS(iterator->_isComposing, false); + } else { ::mongo::StringBuilder sb; - sb << "filler" << i; - testOpts.addOptionChaining(sb.str(), sb.str(), moe::String, "Filler Option"); - } - moe::Value defaultVal(std::string("default")); - moe::Value implicitVal(std::string("implicit")); - optionRef.hidden().setDefault(defaultVal); - optionRef.setImplicit(implicitVal); - - std::vector<moe::OptionDescription> options_vector; - ASSERT_OK(testOpts.getAllOptions(&options_vector)); - - bool foundRef = false; - for(std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); - iterator != options_vector.end(); iterator++) { - - if (iterator->_dottedName == "ref") { - ASSERT_EQUALS(iterator->_singleName, "ref"); - ASSERT_EQUALS(iterator->_type, moe::String); - ASSERT_EQUALS(iterator->_description, "Save this Reference"); - ASSERT_EQUALS(iterator->_isVisible, false); - ASSERT_TRUE(iterator->_default.equal(defaultVal)); - ASSERT_TRUE(iterator->_implicit.equal(implicitVal)); - ASSERT_EQUALS(iterator->_isComposing, false); - foundRef = true; - } - } - if (!foundRef) { - FAIL("Could not find \"ref\" options that we registered"); - } - } - - TEST(ChainingInterface, Basic) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("basic", - "basic", - moe::String, - "Default Option"); - - std::vector<moe::OptionDescription> options_vector; - ASSERT_OK(testOpts.getAllOptions(&options_vector)); - - for(std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); - iterator != options_vector.end(); iterator++) { - - if (iterator->_dottedName == "basic") { - ASSERT_EQUALS(iterator->_singleName, "basic"); - ASSERT_EQUALS(iterator->_type, moe::String); - ASSERT_EQUALS(iterator->_description, "Default Option"); - ASSERT_EQUALS(iterator->_isVisible, true); - ASSERT_TRUE(iterator->_default.isEmpty()); - ASSERT_TRUE(iterator->_implicit.isEmpty()); - ASSERT_EQUALS(iterator->_isComposing, false); - } - else { - ::mongo::StringBuilder sb; - sb << "Found extra option: " << iterator->_dottedName << - " which we did not register"; - FAIL(sb.str()); - } + sb << "Found extra option: " << iterator->_dottedName << " which we did not register"; + FAIL(sb.str()); } } - - TEST(ChainingInterface, Hidden) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("hidden", - "hidden", - moe::String, - "Hidden Option").hidden(); - - std::vector<moe::OptionDescription> options_vector; - ASSERT_OK(testOpts.getAllOptions(&options_vector)); - - for(std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); - iterator != options_vector.end(); iterator++) { - - if (iterator->_dottedName == "hidden") { - ASSERT_EQUALS(iterator->_singleName, "hidden"); - ASSERT_EQUALS(iterator->_type, moe::String); - ASSERT_EQUALS(iterator->_description, "Hidden Option"); - ASSERT_EQUALS(iterator->_isVisible, false); - ASSERT_TRUE(iterator->_default.isEmpty()); - ASSERT_TRUE(iterator->_implicit.isEmpty()); - ASSERT_EQUALS(iterator->_isComposing, false); - } - else { - ::mongo::StringBuilder sb; - sb << "Found extra option: " << iterator->_dottedName << - " which we did not register"; - FAIL(sb.str()); - } +} + +TEST(ChainingInterface, Hidden) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("hidden", "hidden", moe::String, "Hidden Option").hidden(); + + std::vector<moe::OptionDescription> options_vector; + ASSERT_OK(testOpts.getAllOptions(&options_vector)); + + for (std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); + iterator++) { + if (iterator->_dottedName == "hidden") { + ASSERT_EQUALS(iterator->_singleName, "hidden"); + ASSERT_EQUALS(iterator->_type, moe::String); + ASSERT_EQUALS(iterator->_description, "Hidden Option"); + ASSERT_EQUALS(iterator->_isVisible, false); + ASSERT_TRUE(iterator->_default.isEmpty()); + ASSERT_TRUE(iterator->_implicit.isEmpty()); + ASSERT_EQUALS(iterator->_isComposing, false); + } else { + ::mongo::StringBuilder sb; + sb << "Found extra option: " << iterator->_dottedName << " which we did not register"; + FAIL(sb.str()); } } - - TEST(ChainingInterface, DefaultValue) { - OptionsParserTester parser; - moe::Environment environment; - - moe::Value defaultVal(std::string("default")); - - moe::OptionSection testOpts; - testOpts.addOptionChaining("default", - "default", - moe::String, - "Option With Default Value").setDefault(defaultVal); - - std::vector<moe::OptionDescription> options_vector; - ASSERT_OK(testOpts.getAllOptions(&options_vector)); - - for(std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); - iterator != options_vector.end(); iterator++) { - - if (iterator->_dottedName == "default") { - ASSERT_EQUALS(iterator->_singleName, "default"); - ASSERT_EQUALS(iterator->_type, moe::String); - ASSERT_EQUALS(iterator->_description, "Option With Default Value"); - ASSERT_EQUALS(iterator->_isVisible, true); - ASSERT_TRUE(iterator->_default.equal(defaultVal)); - ASSERT_TRUE(iterator->_implicit.isEmpty()); - ASSERT_EQUALS(iterator->_isComposing, false); - } - else { - ::mongo::StringBuilder sb; - sb << "Found extra option: " << iterator->_dottedName << - " which we did not register"; - FAIL(sb.str()); - } +} + +TEST(ChainingInterface, DefaultValue) { + OptionsParserTester parser; + moe::Environment environment; + + moe::Value defaultVal(std::string("default")); + + moe::OptionSection testOpts; + testOpts.addOptionChaining("default", "default", moe::String, "Option With Default Value") + .setDefault(defaultVal); + + std::vector<moe::OptionDescription> options_vector; + ASSERT_OK(testOpts.getAllOptions(&options_vector)); + + for (std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); + iterator++) { + if (iterator->_dottedName == "default") { + ASSERT_EQUALS(iterator->_singleName, "default"); + ASSERT_EQUALS(iterator->_type, moe::String); + ASSERT_EQUALS(iterator->_description, "Option With Default Value"); + ASSERT_EQUALS(iterator->_isVisible, true); + ASSERT_TRUE(iterator->_default.equal(defaultVal)); + ASSERT_TRUE(iterator->_implicit.isEmpty()); + ASSERT_EQUALS(iterator->_isComposing, false); + } else { + ::mongo::StringBuilder sb; + sb << "Found extra option: " << iterator->_dottedName << " which we did not register"; + FAIL(sb.str()); } } - - TEST(ChainingInterface, ImplicitValue) { - OptionsParserTester parser; - moe::Environment environment; - - moe::Value implicitVal(std::string("implicit")); - - moe::OptionSection testOpts; - testOpts.addOptionChaining("implicit", - "implicit", - moe::String, - "Option With Implicit Value").setImplicit(implicitVal); - - std::vector<moe::OptionDescription> options_vector; - ASSERT_OK(testOpts.getAllOptions(&options_vector)); - - for(std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); - iterator != options_vector.end(); iterator++) { - - if (iterator->_dottedName == "implicit") { - ASSERT_EQUALS(iterator->_singleName, "implicit"); - ASSERT_EQUALS(iterator->_type, moe::String); - ASSERT_EQUALS(iterator->_description, "Option With Implicit Value"); - ASSERT_EQUALS(iterator->_isVisible, true); - ASSERT_TRUE(iterator->_default.isEmpty()); - ASSERT_TRUE(iterator->_implicit.equal(implicitVal)); - ASSERT_EQUALS(iterator->_isComposing, false); - } - else { - ::mongo::StringBuilder sb; - sb << "Found extra option: " << iterator->_dottedName << - " which we did not register"; - FAIL(sb.str()); - } +} + +TEST(ChainingInterface, ImplicitValue) { + OptionsParserTester parser; + moe::Environment environment; + + moe::Value implicitVal(std::string("implicit")); + + moe::OptionSection testOpts; + testOpts.addOptionChaining("implicit", "implicit", moe::String, "Option With Implicit Value") + .setImplicit(implicitVal); + + std::vector<moe::OptionDescription> options_vector; + ASSERT_OK(testOpts.getAllOptions(&options_vector)); + + for (std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); + iterator++) { + if (iterator->_dottedName == "implicit") { + ASSERT_EQUALS(iterator->_singleName, "implicit"); + ASSERT_EQUALS(iterator->_type, moe::String); + ASSERT_EQUALS(iterator->_description, "Option With Implicit Value"); + ASSERT_EQUALS(iterator->_isVisible, true); + ASSERT_TRUE(iterator->_default.isEmpty()); + ASSERT_TRUE(iterator->_implicit.equal(implicitVal)); + ASSERT_EQUALS(iterator->_isComposing, false); + } else { + ::mongo::StringBuilder sb; + sb << "Found extra option: " << iterator->_dottedName << " which we did not register"; + FAIL(sb.str()); } } - - TEST(ChainingInterface, Composing) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("setParameter", - "setParameter", - moe::StringVector, - "Multiple Values").composing(); - - std::vector<moe::OptionDescription> options_vector; - ASSERT_OK(testOpts.getAllOptions(&options_vector)); - - for(std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); - iterator != options_vector.end(); iterator++) { - - if (iterator->_dottedName == "setParameter") { - ASSERT_EQUALS(iterator->_singleName, "setParameter"); - ASSERT_EQUALS(iterator->_type, moe::StringVector); - ASSERT_EQUALS(iterator->_description, "Multiple Values"); - ASSERT_EQUALS(iterator->_isVisible, true); - ASSERT_TRUE(iterator->_default.isEmpty()); - ASSERT_TRUE(iterator->_implicit.isEmpty()); - ASSERT_EQUALS(iterator->_isComposing, true); - } - else { - ::mongo::StringBuilder sb; - sb << "Found extra option: " << iterator->_dottedName << - " which we did not register"; - FAIL(sb.str()); - } +} + +TEST(ChainingInterface, Composing) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("setParameter", "setParameter", moe::StringVector, "Multiple Values") + .composing(); + + std::vector<moe::OptionDescription> options_vector; + ASSERT_OK(testOpts.getAllOptions(&options_vector)); + + for (std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); + iterator++) { + if (iterator->_dottedName == "setParameter") { + ASSERT_EQUALS(iterator->_singleName, "setParameter"); + ASSERT_EQUALS(iterator->_type, moe::StringVector); + ASSERT_EQUALS(iterator->_description, "Multiple Values"); + ASSERT_EQUALS(iterator->_isVisible, true); + ASSERT_TRUE(iterator->_default.isEmpty()); + ASSERT_TRUE(iterator->_implicit.isEmpty()); + ASSERT_EQUALS(iterator->_isComposing, true); + } else { + ::mongo::StringBuilder sb; + sb << "Found extra option: " << iterator->_dottedName << " which we did not register"; + FAIL(sb.str()); } } - - TEST(ChainingInterface, Positional) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::String, "Positional") - .positional(1, 1); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional"); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("positional"), &value)); - std::string positional; - ASSERT_OK(value.get(&positional)); - ASSERT_EQUALS(positional, "positional"); - } - - TEST(ChainingInterface, PositionalTooMany) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::String, "Positional") - .positional(1, 1); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional"); - argv.push_back("extrapositional"); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(ChainingInterface, PositionalAndFlag) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::String, "Positional") - .positional(1, 1); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional"); - argv.push_back("--port"); - argv.push_back("5"); - std::map<std::string, std::string> env_map; - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("positional"), &value)); - std::string positional; - ASSERT_OK(value.get(&positional)); - ASSERT_EQUALS(positional, "positional"); - ASSERT_OK(environment.get(moe::Key("port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - } - - TEST(ChainingInterface, PositionalMultiple) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") - .positional(1, 2); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional1"); - argv.push_back("positional2"); - 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("positional"), &value)); - std::vector<std::string> positional; - ASSERT_OK(value.get(&positional)); - std::vector<std::string>::iterator positionalit = positional.begin(); - ASSERT_EQUALS(*positionalit, "positional1"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional2"); - } - - TEST(ChainingInterface, PositionalMultipleExtra) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") - .positional(1, 2); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional1"); - argv.push_back("positional2"); - argv.push_back("positional2"); - std::map<std::string, std::string> env_map; - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(ChainingInterface, PositionalMultipleUnlimited) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") - .positional(1, -1); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional1"); - argv.push_back("positional2"); - argv.push_back("positional3"); - argv.push_back("positional4"); - argv.push_back("positional5"); - 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("positional"), &value)); - std::vector<std::string> positional; - ASSERT_OK(value.get(&positional)); - std::vector<std::string>::iterator positionalit = positional.begin(); - ASSERT_EQUALS(*positionalit, "positional1"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional2"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional3"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional4"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional5"); - } - - TEST(ChainingInterface, PositionalMultipleAndFlag) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") - .positional(1, 2); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional1"); - argv.push_back("--port"); - argv.push_back("5"); - argv.push_back("positional2"); - 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("positional"), &value)); - std::vector<std::string> positional; - ASSERT_OK(value.get(&positional)); - std::vector<std::string>::iterator positionalit = positional.begin(); - ASSERT_EQUALS(*positionalit, "positional1"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional2"); - ASSERT_OK(environment.get(moe::Key("port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - } - - TEST(ChainingInterface, PositionalSingleMultipleUnlimitedAndFlag) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") - .positional(1, 1); - testOpts.addOptionChaining("positional2", "positional2", moe::StringVector, "Positional") - .positional(2, 3); - testOpts.addOptionChaining("positional3", "positional3", moe::StringVector, "Positional") - .positional(4, -1); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("positional1"); - argv.push_back("positional2"); - argv.push_back("positional3"); - argv.push_back("positional4"); - argv.push_back("positional5"); - argv.push_back("positional6"); - argv.push_back("--port"); - argv.push_back("5"); - argv.push_back("positional7"); - argv.push_back("positional8"); - argv.push_back("positional9"); - std::map<std::string, std::string> env_map; - - moe::Value value; - std::vector<std::string>::iterator positionalit; - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("positional1"), &value)); - std::string positionalSingle; - ASSERT_OK(value.get(&positionalSingle)); - ASSERT_EQUALS(positionalSingle, "positional1"); - - ASSERT_OK(environment.get(moe::Key("positional2"), &value)); - std::vector<std::string> positionalMultiple; - ASSERT_OK(value.get(&positionalMultiple)); - positionalit = positionalMultiple.begin(); - ASSERT_EQUALS(*positionalit, "positional2"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional3"); - - ASSERT_OK(environment.get(moe::Key("positional3"), &value)); - std::vector<std::string> positionalUnlimited; - ASSERT_OK(value.get(&positionalUnlimited)); - positionalit = positionalUnlimited.begin(); - ASSERT_EQUALS(*positionalit, "positional4"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional5"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional6"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional7"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional8"); - positionalit++; - ASSERT_EQUALS(*positionalit, "positional9"); - - ASSERT_OK(environment.get(moe::Key("port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - } - - TEST(ChainingInterface, PositionalHoleInRange) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") - .positional(1, 1); - testOpts.addOptionChaining("positional3", "positional2", moe::StringVector, "Positional") - .positional(3, -1); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - std::map<std::string, std::string> env_map; - - moe::Value value; - std::vector<std::string>::iterator positionalit; - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(ChainingInterface, PositionalOverlappingRange) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") - .positional(1, 1); - testOpts.addOptionChaining("positional3", "positional2", moe::StringVector, "Positional") - .positional(1, 2); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - std::map<std::string, std::string> env_map; - - moe::Value value; - std::vector<std::string>::iterator positionalit; - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(ChainingInterface, PositionalOverlappingRangeInfinite) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") - .positional(1, 1); - testOpts.addOptionChaining("positional3", "positional2", moe::StringVector, "Positional") - .positional(1, -1); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - std::map<std::string, std::string> env_map; - - moe::Value value; - std::vector<std::string>::iterator positionalit; - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(ChainingInterface, PositionalMultipleInfinite) { - moe::OptionsParser parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") - .positional(1, -1); - testOpts.addOptionChaining("positional3", "positional2", moe::StringVector, "Positional") - .positional(3, -1); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - std::map<std::string, std::string> env_map; - - moe::Value value; - std::vector<std::string>::iterator positionalit; - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(OptionSources, SourceCommandLine) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - std::string parameter; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("parameter", "parameter", moe::String, "Parameter") - .setSources(moe::SourceCommandLine); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--parameter"); - argv.push_back("allowed"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("parameter"), &value)); - ASSERT_OK(value.get(¶meter)); - ASSERT_EQUALS(parameter, "allowed"); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.json"); - - parser.setConfig("config.json", "{ parameter : \"disallowed\" }"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); - - parser.setConfig("config.ini", "parameter=disallowed"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(OptionSources, SourceINIConfig) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - std::string parameter; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("parameter", "parameter", moe::String, "Parameter") - .setSources(moe::SourceINIConfig); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--parameter"); - argv.push_back("disallowed"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.json"); - - parser.setConfig("config.json", "{ parameter : \"disallowed\" }"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); - - parser.setConfig("config.ini", "parameter=allowed"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("parameter"), &value)); - ASSERT_OK(value.get(¶meter)); - ASSERT_EQUALS(parameter, "allowed"); - - } - - TEST(OptionSources, SourceYAMLConfig) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - std::string parameter; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("parameter", "parameter", moe::String, "Parameter") - .setSources(moe::SourceYAMLConfig); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--parameter"); - argv.push_back("disallowed"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.json"); - - parser.setConfig("config.json", "{ parameter : \"allowed\" }"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("parameter"), &value)); - ASSERT_OK(value.get(¶meter)); - ASSERT_EQUALS(parameter, "allowed"); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); - - parser.setConfig("config.ini", "parameter=disallowed"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(OptionSources, SourceAllConfig) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - std::string parameter; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("parameter", "parameter", moe::String, "Parameter") - .setSources(moe::SourceAllConfig); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--parameter"); - argv.push_back("disallowed"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.json"); - - parser.setConfig("config.json", "{ parameter : \"allowed\" }"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("parameter"), &value)); - ASSERT_OK(value.get(¶meter)); - ASSERT_EQUALS(parameter, "allowed"); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); - - parser.setConfig("config.ini", "parameter=allowed"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("parameter"), &value)); - ASSERT_OK(value.get(¶meter)); - ASSERT_EQUALS(parameter, "allowed"); - } - - TEST(OptionSources, SourceAllLegacy) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - std::string parameter; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("parameter", "parameter", moe::String, "Parameter") - .setSources(moe::SourceAllLegacy); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--parameter"); - argv.push_back("allowed"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("parameter"), &value)); - ASSERT_OK(value.get(¶meter)); - ASSERT_EQUALS(parameter, "allowed"); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.json"); - - parser.setConfig("config.json", "{ parameter : \"disallowed\" }"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); - - parser.setConfig("config.ini", "parameter=allowed"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("parameter"), &value)); - ASSERT_OK(value.get(¶meter)); - ASSERT_EQUALS(parameter, "allowed"); - } - - TEST(OptionSources, SourceAll) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - std::string parameter; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("parameter", "parameter", moe::String, "Parameter") - .setSources(moe::SourceAll); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--parameter"); - argv.push_back("allowed"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("parameter"), &value)); - ASSERT_OK(value.get(¶meter)); - ASSERT_EQUALS(parameter, "allowed"); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.json"); - - parser.setConfig("config.json", "{ parameter : \"allowed\" }"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("parameter"), &value)); - ASSERT_OK(value.get(¶meter)); - ASSERT_EQUALS(parameter, "allowed"); - - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); - - parser.setConfig("config.ini", "parameter=allowed"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("parameter"), &value)); - ASSERT_OK(value.get(¶meter)); - ASSERT_EQUALS(parameter, "allowed"); - } - - TEST(Constraints, NumericRangeConstraint) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - int port; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("port", "port", moe::Int, "Port") - .validRange(1000, 65535); - - environment = moe::Environment(); - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--port"); - argv.push_back("999"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_NOT_OK(environment.validate());; - - environment = moe::Environment(); - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--port"); - argv.push_back("65536"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_NOT_OK(environment.validate());; - - environment = moe::Environment(); - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--port"); - argv.push_back("65535"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.validate());; - ASSERT_OK(environment.get(moe::Key("port"), &value)); - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 65535); - - environment = moe::Environment(); - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--port"); - argv.push_back("1000"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.validate());; - ASSERT_OK(environment.get(moe::Key("port"), &value)); - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 1000); - } - - TEST(Constraints, MutuallyExclusiveConstraint) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("option1", "option1", moe::Switch, "Option1") - .incompatibleWith("section.option2"); - testOpts.addOptionChaining("section.option2", "option2", moe::Switch, "Option2"); - - environment = moe::Environment(); - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--option1"); - argv.push_back("--option2"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_NOT_OK(environment.validate());; - - environment = moe::Environment(); - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--option1"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.validate());; - ASSERT_OK(environment.get(moe::Key("option1"), &value)); - - environment = moe::Environment(); - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--option2"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.validate());; - ASSERT_OK(environment.get(moe::Key("section.option2"), &value)); - } - - TEST(Constraints, RequiresOtherConstraint) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("option1", "option1", moe::Switch, "Option1") - .requires("section.option2"); - testOpts.addOptionChaining("section.option2", "option2", moe::Switch, "Option2"); - - environment = moe::Environment(); - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--option1"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_NOT_OK(environment.validate());; - - environment = moe::Environment(); - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--option1"); - argv.push_back("--option2"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.validate());; - ASSERT_OK(environment.get(moe::Key("option1"), &value)); - ASSERT_OK(environment.get(moe::Key("section.option2"), &value)); - - environment = moe::Environment(); - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--option2"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.validate());; - ASSERT_OK(environment.get(moe::Key("section.option2"), &value)); - } - - TEST(Constraints, StringFormatConstraint) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("option", "option", moe::String, "Option") - .format("[a-z][0-9]", "[character][number]"); - - environment = moe::Environment(); - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--option"); - argv.push_back("aa"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_NOT_OK(environment.validate());; - - environment = moe::Environment(); - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--option"); - argv.push_back("11"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_NOT_OK(environment.validate());; - - environment = moe::Environment(); - argv.clear(); - argv.push_back("binaryname"); - argv.push_back("--option"); - argv.push_back("a1"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.validate()); - ASSERT_OK(environment.get(moe::Key("option"), &value)); - std::string option; - ASSERT_OK(value.get(&option)); - ASSERT_EQUALS(option, "a1"); - } - - TEST(YAMLConfigFile, Basic) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.yaml", "port: 5"); - - 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(YAMLConfigFile, Empty) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.yaml", ""); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(YAMLConfigFile, Override) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - argv.push_back("--port"); - argv.push_back("6"); - std::map<std::string, std::string> env_map; - - - parser.setConfig("config.yaml", "port: 5"); - - 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(YAMLConfigFile, UnregisteredOption) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.yaml", "port: 5"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(YAMLConfigFile, DuplicateOption) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.yaml", "port: 5\nport: 5"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(YAMLConfigFile, TypeChecking) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("stringVectorVal", "stringVectorVal", moe::StringVector, - "StringVectorVal"); - testOpts.addOptionChaining("boolVal", "boolVal", moe::Bool, "BoolVal"); - testOpts.addOptionChaining("doubleVal", "doubleVal", moe::Double, "DoubleVal"); - testOpts.addOptionChaining("intVal", "intVal", moe::Int, "IntVal"); - testOpts.addOptionChaining("longVal", "longVal", moe::Long, "LongVal"); - testOpts.addOptionChaining("stringVal", "stringVal", moe::String, "StringVal"); - testOpts.addOptionChaining("unsignedLongLongVal", "unsignedLongLongVal", - moe::UnsignedLongLong, "UnsignedLongLongVal"); - testOpts.addOptionChaining("unsignedVal", "unsignedVal", moe::Unsigned, "UnsignedVal"); - testOpts.addOptionChaining("switchVal", "switchVal", moe::Switch, "SwitchVal"); - - 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; - - // Test StringVector type - std::vector<std::string> stringVectorVal; - - parser.setConfig("config.json", "stringVectorVal : \"scalar\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "stringVectorVal : \"true\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "stringVectorVal : \"5\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "stringVectorVal : [ [ \"string\" ], true, 1, 1.0 ]"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as a string vector type and treat it as an array of strings, even if the - // elements are not surrounded by quotes - environment = moe::Environment(); - parser.setConfig("config.json", "stringVectorVal : [ \"string\", bare, true, 1, 1.0 ]"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("stringVectorVal"), &value)); - std::vector<std::string>::iterator stringVectorValIt; - ASSERT_OK(value.get(&stringVectorVal)); - stringVectorValIt = stringVectorVal.begin(); - ASSERT_EQUALS(*stringVectorValIt, "string"); - stringVectorValIt++; - ASSERT_EQUALS(*stringVectorValIt, "bare"); - stringVectorValIt++; - ASSERT_EQUALS(*stringVectorValIt, "true"); - stringVectorValIt++; - ASSERT_EQUALS(*stringVectorValIt, "1"); - stringVectorValIt++; - ASSERT_EQUALS(*stringVectorValIt, "1.0"); - - // Test Bool type - bool boolVal; - environment = moe::Environment(); - parser.setConfig("config.json", "boolVal : \"lies\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "boolVal : truth"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "boolVal : 1"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as a bool type and try to convert it to a bool, even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "boolVal : \"true\""); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("boolVal"), &value)); - ASSERT_OK(value.get(&boolVal)); - ASSERT_EQUALS(boolVal, true); - environment = moe::Environment(); - parser.setConfig("config.json", "boolVal : false"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("boolVal"), &value)); - ASSERT_OK(value.get(&boolVal)); - ASSERT_EQUALS(boolVal, false); - - // Test Double type - double doubleVal; - environment = moe::Environment(); - parser.setConfig("config.json", "doubleVal : \"double the monkeys\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "doubleVal : true"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as a double type and try to convert it to a double, even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "doubleVal : 1.5"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, 1.5); - environment = moe::Environment(); - parser.setConfig("config.json", "doubleVal : -1.5"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, -1.5); - environment = moe::Environment(); - parser.setConfig("config.json", "doubleVal : \"3.14\""); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, 3.14); - environment = moe::Environment(); - parser.setConfig("config.json", "doubleVal : \"-3.14\""); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, -3.14); - - // Test Int type - int intVal; - environment = moe::Environment(); - parser.setConfig("config.json", "intVal : \"hungry hippos\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "intVal : 1.5"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "intVal : 18446744073709551617"); // 2^64 + 1 - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "intVal : true"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as an int type and try to convert it to a int, even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "intVal : \"5\""); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("intVal"), &value)); - ASSERT_OK(value.get(&intVal)); - ASSERT_EQUALS(intVal, 5); - - environment = moe::Environment(); - parser.setConfig("config.json", "intVal : \"-5\""); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("intVal"), &value)); - ASSERT_OK(value.get(&intVal)); - ASSERT_EQUALS(intVal, -5); - - // Test Long type - long longVal; - environment = moe::Environment(); - parser.setConfig("config.json", "longVal : \"in an eating race\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "longVal : 1.5"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "longVal : 18446744073709551617"); // 2^64 + 1 - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "longVal : true"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as a long type and try to convert it to a long, even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "longVal : \"5\""); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("longVal"), &value)); - ASSERT_OK(value.get(&longVal)); - ASSERT_EQUALS(longVal, 5); - - environment = moe::Environment(); - parser.setConfig("config.json", "longVal : \"-5\""); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("longVal"), &value)); - ASSERT_OK(value.get(&longVal)); - ASSERT_EQUALS(longVal, -5); - - // Test String type - std::string stringVal; - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as a string type and treat it as a string, even if the element is not - // surrounded by quotes - environment = moe::Environment(); - parser.setConfig("config.json", "stringVal :"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); - ASSERT_OK(value.get(&stringVal)); - ASSERT_EQUALS(stringVal, ""); - - environment = moe::Environment(); - parser.setConfig("config.json", "stringVal : \"1000\""); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); - ASSERT_OK(value.get(&stringVal)); - ASSERT_EQUALS(stringVal, "1000"); - - environment = moe::Environment(); - parser.setConfig("config.json", "stringVal : wat man"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); - ASSERT_OK(value.get(&stringVal)); - ASSERT_EQUALS(stringVal, "wat man"); - - environment = moe::Environment(); - parser.setConfig("config.json", "stringVal : true 1 string 1.0"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); - ASSERT_OK(value.get(&stringVal)); - ASSERT_EQUALS(stringVal, "true 1 string 1.0"); - - // Test UnsignedLongLong type - unsigned long long unsignedLongLongVal; - environment = moe::Environment(); - parser.setConfig("config.json", "unsignedLongLongVal : \"unsigned hungry hippos\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "unsignedLongLongVal : 1.5"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", - "unsignedLongLongVal : 18446744073709551617"); // 2^64 + 1 - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "unsignedLongLongVal : true"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "unsignedLongLongVal : \"-5\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as an unsigned long long type and try to convert it to an unsigned long long, - // even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "unsignedLongLongVal : \"5\""); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); - ASSERT_OK(value.get(&unsignedLongLongVal)); - ASSERT_EQUALS(unsignedLongLongVal, 5ULL); - - // Test Unsigned type - unsigned unsignedVal; - environment = moe::Environment(); - parser.setConfig("config.json", "unsignedVal : \"unsigned hungry hippos\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "unsignedVal : 1.5"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "unsignedVal : 18446744073709551617"); // 2^64 + 1 - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "unsignedVal : true"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "unsignedVal : \"-5\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as an unsigned type and try to convert it to an unsigned, even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "unsignedVal : \"5\""); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); - ASSERT_OK(value.get(&unsignedVal)); - ASSERT_EQUALS(unsignedVal, 5U); - - // Test Switch type - bool switchVal; - environment = moe::Environment(); - parser.setConfig("config.json", "switchVal : \"lies\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "switchVal : truth"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - environment = moe::Environment(); - parser.setConfig("config.json", "switchVal : 1"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // The YAML parser treats everything as a string, so we just take anything that was - // specified as a switch type and try to convert it to a bool, even if it was quoted - environment = moe::Environment(); - parser.setConfig("config.json", "switchVal : \"true\""); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("switchVal"), &value)); - ASSERT_OK(value.get(&switchVal)); - ASSERT_EQUALS(switchVal, true); - environment = moe::Environment(); - parser.setConfig("config.json", "switchVal : false"); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("switchVal"), &switchVal)); - ASSERT_FALSE(switchVal); - } - - TEST(YAMLConfigFile, Nested) { +} + +TEST(ChainingInterface, Positional) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::String, "Positional") + .positional(1, 1); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional"); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("positional"), &value)); + std::string positional; + ASSERT_OK(value.get(&positional)); + ASSERT_EQUALS(positional, "positional"); +} + +TEST(ChainingInterface, PositionalTooMany) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::String, "Positional") + .positional(1, 1); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional"); + argv.push_back("extrapositional"); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(ChainingInterface, PositionalAndFlag) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::String, "Positional") + .positional(1, 1); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional"); + argv.push_back("--port"); + argv.push_back("5"); + std::map<std::string, std::string> env_map; + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("positional"), &value)); + std::string positional; + ASSERT_OK(value.get(&positional)); + ASSERT_EQUALS(positional, "positional"); + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); +} + +TEST(ChainingInterface, PositionalMultiple) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") + .positional(1, 2); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional1"); + argv.push_back("positional2"); + 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("positional"), &value)); + std::vector<std::string> positional; + ASSERT_OK(value.get(&positional)); + std::vector<std::string>::iterator positionalit = positional.begin(); + ASSERT_EQUALS(*positionalit, "positional1"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional2"); +} + +TEST(ChainingInterface, PositionalMultipleExtra) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") + .positional(1, 2); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional1"); + argv.push_back("positional2"); + argv.push_back("positional2"); + std::map<std::string, std::string> env_map; + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(ChainingInterface, PositionalMultipleUnlimited) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") + .positional(1, -1); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional1"); + argv.push_back("positional2"); + argv.push_back("positional3"); + argv.push_back("positional4"); + argv.push_back("positional5"); + 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("positional"), &value)); + std::vector<std::string> positional; + ASSERT_OK(value.get(&positional)); + std::vector<std::string>::iterator positionalit = positional.begin(); + ASSERT_EQUALS(*positionalit, "positional1"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional2"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional3"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional4"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional5"); +} + +TEST(ChainingInterface, PositionalMultipleAndFlag) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional", "positional", moe::StringVector, "Positional") + .positional(1, 2); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional1"); + argv.push_back("--port"); + argv.push_back("5"); + argv.push_back("positional2"); + 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("positional"), &value)); + std::vector<std::string> positional; + ASSERT_OK(value.get(&positional)); + std::vector<std::string>::iterator positionalit = positional.begin(); + ASSERT_EQUALS(*positionalit, "positional1"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional2"); + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); +} + +TEST(ChainingInterface, PositionalSingleMultipleUnlimitedAndFlag) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") + .positional(1, 1); + testOpts.addOptionChaining("positional2", "positional2", moe::StringVector, "Positional") + .positional(2, 3); + testOpts.addOptionChaining("positional3", "positional3", moe::StringVector, "Positional") + .positional(4, -1); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("positional1"); + argv.push_back("positional2"); + argv.push_back("positional3"); + argv.push_back("positional4"); + argv.push_back("positional5"); + argv.push_back("positional6"); + argv.push_back("--port"); + argv.push_back("5"); + argv.push_back("positional7"); + argv.push_back("positional8"); + argv.push_back("positional9"); + std::map<std::string, std::string> env_map; + + moe::Value value; + std::vector<std::string>::iterator positionalit; + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("positional1"), &value)); + std::string positionalSingle; + ASSERT_OK(value.get(&positionalSingle)); + ASSERT_EQUALS(positionalSingle, "positional1"); + + ASSERT_OK(environment.get(moe::Key("positional2"), &value)); + std::vector<std::string> positionalMultiple; + ASSERT_OK(value.get(&positionalMultiple)); + positionalit = positionalMultiple.begin(); + ASSERT_EQUALS(*positionalit, "positional2"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional3"); + + ASSERT_OK(environment.get(moe::Key("positional3"), &value)); + std::vector<std::string> positionalUnlimited; + ASSERT_OK(value.get(&positionalUnlimited)); + positionalit = positionalUnlimited.begin(); + ASSERT_EQUALS(*positionalit, "positional4"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional5"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional6"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional7"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional8"); + positionalit++; + ASSERT_EQUALS(*positionalit, "positional9"); + + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); +} + +TEST(ChainingInterface, PositionalHoleInRange) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") + .positional(1, 1); + testOpts.addOptionChaining("positional3", "positional2", moe::StringVector, "Positional") + .positional(3, -1); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + std::map<std::string, std::string> env_map; + + moe::Value value; + std::vector<std::string>::iterator positionalit; + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(ChainingInterface, PositionalOverlappingRange) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") + .positional(1, 1); + testOpts.addOptionChaining("positional3", "positional2", moe::StringVector, "Positional") + .positional(1, 2); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + std::map<std::string, std::string> env_map; + + moe::Value value; + std::vector<std::string>::iterator positionalit; + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(ChainingInterface, PositionalOverlappingRangeInfinite) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") + .positional(1, 1); + testOpts.addOptionChaining("positional3", "positional2", moe::StringVector, "Positional") + .positional(1, -1); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + std::map<std::string, std::string> env_map; + + moe::Value value; + std::vector<std::string>::iterator positionalit; + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(ChainingInterface, PositionalMultipleInfinite) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("positional1", "positional1", moe::String, "Positional") + .positional(1, -1); + testOpts.addOptionChaining("positional3", "positional2", moe::StringVector, "Positional") + .positional(3, -1); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + std::map<std::string, std::string> env_map; + + moe::Value value; + std::vector<std::string>::iterator positionalit; + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(OptionSources, SourceCommandLine) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + std::string parameter; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("parameter", "parameter", moe::String, "Parameter") + .setSources(moe::SourceCommandLine); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--parameter"); + argv.push_back("allowed"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("parameter"), &value)); + ASSERT_OK(value.get(¶meter)); + ASSERT_EQUALS(parameter, "allowed"); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.json"); + + parser.setConfig("config.json", "{ parameter : \"disallowed\" }"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); + + parser.setConfig("config.ini", "parameter=disallowed"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(OptionSources, SourceINIConfig) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + std::string parameter; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("parameter", "parameter", moe::String, "Parameter") + .setSources(moe::SourceINIConfig); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--parameter"); + argv.push_back("disallowed"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.json"); + + parser.setConfig("config.json", "{ parameter : \"disallowed\" }"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); + + parser.setConfig("config.ini", "parameter=allowed"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("parameter"), &value)); + ASSERT_OK(value.get(¶meter)); + ASSERT_EQUALS(parameter, "allowed"); +} + +TEST(OptionSources, SourceYAMLConfig) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + std::string parameter; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("parameter", "parameter", moe::String, "Parameter") + .setSources(moe::SourceYAMLConfig); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--parameter"); + argv.push_back("disallowed"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.json"); + + parser.setConfig("config.json", "{ parameter : \"allowed\" }"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("parameter"), &value)); + ASSERT_OK(value.get(¶meter)); + ASSERT_EQUALS(parameter, "allowed"); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); + + parser.setConfig("config.ini", "parameter=disallowed"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(OptionSources, SourceAllConfig) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + std::string parameter; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("parameter", "parameter", moe::String, "Parameter") + .setSources(moe::SourceAllConfig); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--parameter"); + argv.push_back("disallowed"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.json"); + + parser.setConfig("config.json", "{ parameter : \"allowed\" }"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("parameter"), &value)); + ASSERT_OK(value.get(¶meter)); + ASSERT_EQUALS(parameter, "allowed"); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); + + parser.setConfig("config.ini", "parameter=allowed"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("parameter"), &value)); + ASSERT_OK(value.get(¶meter)); + ASSERT_EQUALS(parameter, "allowed"); +} + +TEST(OptionSources, SourceAllLegacy) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + std::string parameter; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("parameter", "parameter", moe::String, "Parameter") + .setSources(moe::SourceAllLegacy); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--parameter"); + argv.push_back("allowed"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("parameter"), &value)); + ASSERT_OK(value.get(¶meter)); + ASSERT_EQUALS(parameter, "allowed"); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.json"); + + parser.setConfig("config.json", "{ parameter : \"disallowed\" }"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); + + parser.setConfig("config.ini", "parameter=allowed"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("parameter"), &value)); + ASSERT_OK(value.get(¶meter)); + ASSERT_EQUALS(parameter, "allowed"); +} + +TEST(OptionSources, SourceAll) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + std::string parameter; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("parameter", "parameter", moe::String, "Parameter") + .setSources(moe::SourceAll); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--parameter"); + argv.push_back("allowed"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("parameter"), &value)); + ASSERT_OK(value.get(¶meter)); + ASSERT_EQUALS(parameter, "allowed"); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.json"); + + parser.setConfig("config.json", "{ parameter : \"allowed\" }"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("parameter"), &value)); + ASSERT_OK(value.get(¶meter)); + ASSERT_EQUALS(parameter, "allowed"); + + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); + + parser.setConfig("config.ini", "parameter=allowed"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("parameter"), &value)); + ASSERT_OK(value.get(¶meter)); + ASSERT_EQUALS(parameter, "allowed"); +} + +TEST(Constraints, NumericRangeConstraint) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + int port; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("port", "port", moe::Int, "Port").validRange(1000, 65535); + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--port"); + argv.push_back("999"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_NOT_OK(environment.validate()); + ; + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--port"); + argv.push_back("65536"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_NOT_OK(environment.validate()); + ; + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--port"); + argv.push_back("65535"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.validate()); + ; + ASSERT_OK(environment.get(moe::Key("port"), &value)); + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 65535); + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--port"); + argv.push_back("1000"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.validate()); + ; + ASSERT_OK(environment.get(moe::Key("port"), &value)); + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 1000); +} + +TEST(Constraints, MutuallyExclusiveConstraint) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("option1", "option1", moe::Switch, "Option1") + .incompatibleWith("section.option2"); + testOpts.addOptionChaining("section.option2", "option2", moe::Switch, "Option2"); + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--option1"); + argv.push_back("--option2"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_NOT_OK(environment.validate()); + ; + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--option1"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.validate()); + ; + ASSERT_OK(environment.get(moe::Key("option1"), &value)); + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--option2"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.validate()); + ; + ASSERT_OK(environment.get(moe::Key("section.option2"), &value)); +} + +TEST(Constraints, RequiresOtherConstraint) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("option1", "option1", moe::Switch, "Option1") + .requires("section.option2"); + testOpts.addOptionChaining("section.option2", "option2", moe::Switch, "Option2"); + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--option1"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_NOT_OK(environment.validate()); + ; + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--option1"); + argv.push_back("--option2"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.validate()); + ; + ASSERT_OK(environment.get(moe::Key("option1"), &value)); + ASSERT_OK(environment.get(moe::Key("section.option2"), &value)); + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--option2"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.validate()); + ; + ASSERT_OK(environment.get(moe::Key("section.option2"), &value)); +} + +TEST(Constraints, StringFormatConstraint) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("option", "option", moe::String, "Option") + .format("[a-z][0-9]", "[character][number]"); + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--option"); + argv.push_back("aa"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_NOT_OK(environment.validate()); + ; + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--option"); + argv.push_back("11"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_NOT_OK(environment.validate()); + ; + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--option"); + argv.push_back("a1"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.validate()); + ASSERT_OK(environment.get(moe::Key("option"), &value)); + std::string option; + ASSERT_OK(value.get(&option)); + ASSERT_EQUALS(option, "a1"); +} + +TEST(YAMLConfigFile, Basic) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", "port: 5"); + + 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(YAMLConfigFile, Empty) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", ""); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(YAMLConfigFile, Override) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + argv.push_back("--port"); + argv.push_back("6"); + std::map<std::string, std::string> env_map; + + + parser.setConfig("config.yaml", "port: 5"); + + 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(YAMLConfigFile, UnregisteredOption) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", "port: 5"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(YAMLConfigFile, DuplicateOption) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", "port: 5\nport: 5"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(YAMLConfigFile, TypeChecking) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining( + "stringVectorVal", "stringVectorVal", moe::StringVector, "StringVectorVal"); + testOpts.addOptionChaining("boolVal", "boolVal", moe::Bool, "BoolVal"); + testOpts.addOptionChaining("doubleVal", "doubleVal", moe::Double, "DoubleVal"); + testOpts.addOptionChaining("intVal", "intVal", moe::Int, "IntVal"); + testOpts.addOptionChaining("longVal", "longVal", moe::Long, "LongVal"); + testOpts.addOptionChaining("stringVal", "stringVal", moe::String, "StringVal"); + testOpts.addOptionChaining( + "unsignedLongLongVal", "unsignedLongLongVal", moe::UnsignedLongLong, "UnsignedLongLongVal"); + testOpts.addOptionChaining("unsignedVal", "unsignedVal", moe::Unsigned, "UnsignedVal"); + testOpts.addOptionChaining("switchVal", "switchVal", moe::Switch, "SwitchVal"); + + 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; + + // Test StringVector type + std::vector<std::string> stringVectorVal; + + parser.setConfig("config.json", "stringVectorVal : \"scalar\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "stringVectorVal : \"true\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "stringVectorVal : \"5\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "stringVectorVal : [ [ \"string\" ], true, 1, 1.0 ]"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as a string vector type and treat it as an array of strings, even if the + // elements are not surrounded by quotes + environment = moe::Environment(); + parser.setConfig("config.json", "stringVectorVal : [ \"string\", bare, true, 1, 1.0 ]"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("stringVectorVal"), &value)); + std::vector<std::string>::iterator stringVectorValIt; + ASSERT_OK(value.get(&stringVectorVal)); + stringVectorValIt = stringVectorVal.begin(); + ASSERT_EQUALS(*stringVectorValIt, "string"); + stringVectorValIt++; + ASSERT_EQUALS(*stringVectorValIt, "bare"); + stringVectorValIt++; + ASSERT_EQUALS(*stringVectorValIt, "true"); + stringVectorValIt++; + ASSERT_EQUALS(*stringVectorValIt, "1"); + stringVectorValIt++; + ASSERT_EQUALS(*stringVectorValIt, "1.0"); + + // Test Bool type + bool boolVal; + environment = moe::Environment(); + parser.setConfig("config.json", "boolVal : \"lies\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "boolVal : truth"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "boolVal : 1"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as a bool type and try to convert it to a bool, even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "boolVal : \"true\""); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("boolVal"), &value)); + ASSERT_OK(value.get(&boolVal)); + ASSERT_EQUALS(boolVal, true); + environment = moe::Environment(); + parser.setConfig("config.json", "boolVal : false"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("boolVal"), &value)); + ASSERT_OK(value.get(&boolVal)); + ASSERT_EQUALS(boolVal, false); + + // Test Double type + double doubleVal; + environment = moe::Environment(); + parser.setConfig("config.json", "doubleVal : \"double the monkeys\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "doubleVal : true"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as a double type and try to convert it to a double, even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "doubleVal : 1.5"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, 1.5); + environment = moe::Environment(); + parser.setConfig("config.json", "doubleVal : -1.5"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, -1.5); + environment = moe::Environment(); + parser.setConfig("config.json", "doubleVal : \"3.14\""); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, 3.14); + environment = moe::Environment(); + parser.setConfig("config.json", "doubleVal : \"-3.14\""); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, -3.14); + + // Test Int type + int intVal; + environment = moe::Environment(); + parser.setConfig("config.json", "intVal : \"hungry hippos\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "intVal : 1.5"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "intVal : 18446744073709551617"); // 2^64 + 1 + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "intVal : true"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as an int type and try to convert it to a int, even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "intVal : \"5\""); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("intVal"), &value)); + ASSERT_OK(value.get(&intVal)); + ASSERT_EQUALS(intVal, 5); + + environment = moe::Environment(); + parser.setConfig("config.json", "intVal : \"-5\""); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("intVal"), &value)); + ASSERT_OK(value.get(&intVal)); + ASSERT_EQUALS(intVal, -5); + + // Test Long type + long longVal; + environment = moe::Environment(); + parser.setConfig("config.json", "longVal : \"in an eating race\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "longVal : 1.5"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "longVal : 18446744073709551617"); // 2^64 + 1 + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "longVal : true"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as a long type and try to convert it to a long, even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "longVal : \"5\""); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("longVal"), &value)); + ASSERT_OK(value.get(&longVal)); + ASSERT_EQUALS(longVal, 5); + + environment = moe::Environment(); + parser.setConfig("config.json", "longVal : \"-5\""); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("longVal"), &value)); + ASSERT_OK(value.get(&longVal)); + ASSERT_EQUALS(longVal, -5); + + // Test String type + std::string stringVal; + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as a string type and treat it as a string, even if the element is not + // surrounded by quotes + environment = moe::Environment(); + parser.setConfig("config.json", "stringVal :"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); + ASSERT_OK(value.get(&stringVal)); + ASSERT_EQUALS(stringVal, ""); + + environment = moe::Environment(); + parser.setConfig("config.json", "stringVal : \"1000\""); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); + ASSERT_OK(value.get(&stringVal)); + ASSERT_EQUALS(stringVal, "1000"); + + environment = moe::Environment(); + parser.setConfig("config.json", "stringVal : wat man"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); + ASSERT_OK(value.get(&stringVal)); + ASSERT_EQUALS(stringVal, "wat man"); + + environment = moe::Environment(); + parser.setConfig("config.json", "stringVal : true 1 string 1.0"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("stringVal"), &value)); + ASSERT_OK(value.get(&stringVal)); + ASSERT_EQUALS(stringVal, "true 1 string 1.0"); + + // Test UnsignedLongLong type + unsigned long long unsignedLongLongVal; + environment = moe::Environment(); + parser.setConfig("config.json", "unsignedLongLongVal : \"unsigned hungry hippos\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "unsignedLongLongVal : 1.5"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "unsignedLongLongVal : 18446744073709551617"); // 2^64 + 1 + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "unsignedLongLongVal : true"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "unsignedLongLongVal : \"-5\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as an unsigned long long type and try to convert it to an unsigned long long, + // even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "unsignedLongLongVal : \"5\""); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); + ASSERT_OK(value.get(&unsignedLongLongVal)); + ASSERT_EQUALS(unsignedLongLongVal, 5ULL); + + // Test Unsigned type + unsigned unsignedVal; + environment = moe::Environment(); + parser.setConfig("config.json", "unsignedVal : \"unsigned hungry hippos\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "unsignedVal : 1.5"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "unsignedVal : 18446744073709551617"); // 2^64 + 1 + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "unsignedVal : true"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "unsignedVal : \"-5\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as an unsigned type and try to convert it to an unsigned, even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "unsignedVal : \"5\""); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); + ASSERT_OK(value.get(&unsignedVal)); + ASSERT_EQUALS(unsignedVal, 5U); + + // Test Switch type + bool switchVal; + environment = moe::Environment(); + parser.setConfig("config.json", "switchVal : \"lies\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "switchVal : truth"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + parser.setConfig("config.json", "switchVal : 1"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // The YAML parser treats everything as a string, so we just take anything that was + // specified as a switch type and try to convert it to a bool, even if it was quoted + environment = moe::Environment(); + parser.setConfig("config.json", "switchVal : \"true\""); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("switchVal"), &value)); + ASSERT_OK(value.get(&switchVal)); + ASSERT_EQUALS(switchVal, true); + environment = moe::Environment(); + parser.setConfig("config.json", "switchVal : false"); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("switchVal"), &switchVal)); + ASSERT_FALSE(switchVal); +} + +TEST(YAMLConfigFile, Nested) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("nested.port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", "nested:\n port: 5"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("nested.port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); +} + +TEST(YAMLConfigFile, Dotted) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("dotted.port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", "dotted.port: 5"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("dotted.port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); +} + +TEST(YAMLConfigFile, DottedAndNested) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("dottednested.var1", "var1", moe::Int, "Var1"); + testOpts.addOptionChaining("dottednested.var2", "var2", moe::Int, "Var2"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", "dottednested.var1: 5\ndottednested:\n var2: 6"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("dottednested.var1"), &value)); + int var1; + ASSERT_OK(value.get(&var1)); + ASSERT_EQUALS(var1, 5); + ASSERT_OK(environment.get(moe::Key("dottednested.var2"), &value)); + int var2; + ASSERT_OK(value.get(&var2)); + ASSERT_EQUALS(var2, 6); +} + +// If configuration file contains a deprecated dotted name, value will be set in the +// environment with the canonical name as the key. Deprecated dotted name will not appear +// in result environment. +TEST(YAMLConfigFile, DeprecatedDottedNameDeprecatedOnly) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("dotted.canonical", "var1", moe::Int, "Var1", "dotted.deprecated"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", "dotted.deprecated: 6"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("dotted.canonical"), &value)); + int var1; + ASSERT_OK(value.get(&var1)); + ASSERT_EQUALS(var1, 6); + ASSERT_FALSE(environment.count(moe::Key("dotted.deprecated"))); +} + +// Deprecated dotted name cannot be the same as the canonical name. +TEST(YAMLConfigFile, DeprecatedDottedNameSameAsCanonicalDottedName) { + moe::OptionSection testOpts; + ASSERT_THROWS(testOpts.addOptionChaining( + "dotted.canonical", "var1", moe::Int, "Var1", "dotted.canonical"), + ::mongo::DBException); +} + +// Deprecated dotted name cannot be the empty string. +TEST(YAMLConfigFile, DeprecatedDottedNameEmptyString) { + moe::OptionSection testOpts; + ASSERT_THROWS(testOpts.addOptionChaining("dotted.canonical", "var1", moe::Int, "Var1", ""), + ::mongo::DBException); +} + +// Deprecated dotted name cannot be the same as another option's dotted name. +TEST(YAMLConfigFile, DeprecatedDottedNameSameAsOtherOptionsDottedName) { + moe::OptionSection testOpts; + testOpts.addOptionChaining("dotted.canonical1", "var1", moe::Int, "Var1"); + ASSERT_THROWS(testOpts.addOptionChaining( + "dotted.canonical2", "var2", moe::Int, "Var2", "dotted.canonical1"), + ::mongo::DBException); +} + +// Deprecated dotted name cannot be the same as another option's deprecated dotted name. +TEST(YAMLConfigFile, DeprecatedDottedNameSameAsOtherOptionsDeprecatedDottedName) { + moe::OptionSection testOpts; + testOpts.addOptionChaining("dotted.canonical1", "var1", moe::Int, "Var1", "dotted.deprecated1"); + ASSERT_THROWS(testOpts.addOptionChaining( + "dotted.canonical2", "var2", moe::Int, "Var2", "dotted.deprecated1"), + ::mongo::DBException); +} + +// It is an error to have both canonical and deprecated dotted names in the same +// configuration file. +TEST(YAMLConfigFile, DeprecatedDottedNameCanonicalAndDeprecated) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("dotted.canonical", "var1", moe::Int, "Var1", "dotted.deprecated"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", + "dotted.canonical: 5\n" + "dotted.deprecated: 6"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +// An option can have multiple deprecated dotted names. +TEST(YAMLConfigFile, DeprecatedDottedNameMultipleDeprecated) { + std::vector<std::string> deprecatedDottedNames; + deprecatedDottedNames.push_back("dotted.deprecated1"); + deprecatedDottedNames.push_back("dotted.deprecated2"); + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("dotted.canonical", "var1", moe::Int, "Var1", deprecatedDottedNames); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + + // Parse 2 files - each containing a different deprecated dotted name. + for (std::vector<std::string>::const_iterator i = deprecatedDottedNames.begin(); + i != deprecatedDottedNames.end(); + ++i) { OptionsParserTester parser; moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("nested.port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); std::map<std::string, std::string> env_map; - parser.setConfig("config.yaml", "nested:\n port: 5"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - moe::Value value; - ASSERT_OK(environment.get(moe::Key("nested.port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - } - - TEST(YAMLConfigFile, Dotted) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("dotted.port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.yaml", "dotted.port: 5"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - moe::Value value; - ASSERT_OK(environment.get(moe::Key("dotted.port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - } - - TEST(YAMLConfigFile, DottedAndNested) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("dottednested.var1", "var1", moe::Int, "Var1"); - testOpts.addOptionChaining("dottednested.var2", "var2", moe::Int, "Var2"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.yaml", - "dottednested.var1: 5\ndottednested:\n var2: 6"); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - moe::Value value; - ASSERT_OK(environment.get(moe::Key("dottednested.var1"), &value)); - int var1; - ASSERT_OK(value.get(&var1)); - ASSERT_EQUALS(var1, 5); - ASSERT_OK(environment.get(moe::Key("dottednested.var2"), &value)); - int var2; - ASSERT_OK(value.get(&var2)); - ASSERT_EQUALS(var2, 6); - } - - // If configuration file contains a deprecated dotted name, value will be set in the - // environment with the canonical name as the key. Deprecated dotted name will not appear - // in result environment. - TEST(YAMLConfigFile, DeprecatedDottedNameDeprecatedOnly) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("dotted.canonical", "var1", moe::Int, "Var1", - "dotted.deprecated"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.yaml", - "dotted.deprecated: 6"); + ::mongo::StringBuilder sb; + sb << *i << ": 6"; + parser.setConfig("config.yaml", sb.str()); ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); moe::Value value; @@ -3691,814 +3747,731 @@ namespace { int var1; ASSERT_OK(value.get(&var1)); ASSERT_EQUALS(var1, 6); - ASSERT_FALSE(environment.count(moe::Key("dotted.deprecated"))); - } - - // Deprecated dotted name cannot be the same as the canonical name. - TEST(YAMLConfigFile, DeprecatedDottedNameSameAsCanonicalDottedName) { - moe::OptionSection testOpts; - ASSERT_THROWS(testOpts.addOptionChaining("dotted.canonical", "var1", moe::Int, "Var1", - "dotted.canonical"), ::mongo::DBException); - } - - // Deprecated dotted name cannot be the empty string. - TEST(YAMLConfigFile, DeprecatedDottedNameEmptyString) { - moe::OptionSection testOpts; - ASSERT_THROWS(testOpts.addOptionChaining("dotted.canonical", "var1", moe::Int, "Var1", ""), - ::mongo::DBException); - } - - // Deprecated dotted name cannot be the same as another option's dotted name. - TEST(YAMLConfigFile, DeprecatedDottedNameSameAsOtherOptionsDottedName) { - moe::OptionSection testOpts; - testOpts.addOptionChaining("dotted.canonical1", "var1", moe::Int, "Var1"); - ASSERT_THROWS(testOpts.addOptionChaining("dotted.canonical2", "var2", moe::Int, "Var2", - "dotted.canonical1"), ::mongo::DBException); - } - - // Deprecated dotted name cannot be the same as another option's deprecated dotted name. - TEST(YAMLConfigFile, DeprecatedDottedNameSameAsOtherOptionsDeprecatedDottedName) { - moe::OptionSection testOpts; - testOpts.addOptionChaining("dotted.canonical1", "var1", moe::Int, "Var1", - "dotted.deprecated1"); - ASSERT_THROWS(testOpts.addOptionChaining("dotted.canonical2", "var2", moe::Int, "Var2", - "dotted.deprecated1"), ::mongo::DBException); - } - - // It is an error to have both canonical and deprecated dotted names in the same - // configuration file. - TEST(YAMLConfigFile, DeprecatedDottedNameCanonicalAndDeprecated) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("dotted.canonical", "var1", moe::Int, "Var1", - "dotted.deprecated"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.yaml", - "dotted.canonical: 5\n" - "dotted.deprecated: 6"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - // An option can have multiple deprecated dotted names. - TEST(YAMLConfigFile, DeprecatedDottedNameMultipleDeprecated) { - std::vector<std::string> deprecatedDottedNames; - deprecatedDottedNames.push_back("dotted.deprecated1"); - deprecatedDottedNames.push_back("dotted.deprecated2"); - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("dotted.canonical", "var1", moe::Int, "Var1", - deprecatedDottedNames); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - - // Parse 2 files - each containing a different deprecated dotted name. - for (std::vector<std::string>::const_iterator i = deprecatedDottedNames.begin(); - i != deprecatedDottedNames.end(); ++i) { - OptionsParserTester parser; - moe::Environment environment; - std::map<std::string, std::string> env_map; - - ::mongo::StringBuilder sb; - sb << *i << ": 6"; - parser.setConfig("config.yaml", sb.str()); - - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - moe::Value value; - ASSERT_OK(environment.get(moe::Key("dotted.canonical"), &value)); - int var1; - ASSERT_OK(value.get(&var1)); - ASSERT_EQUALS(var1, 6); - ASSERT_FALSE(environment.count(moe::Key(deprecatedDottedNames[0]))); - ASSERT_FALSE(environment.count(moe::Key(deprecatedDottedNames[1]))); - } - - // It is an error to have multiple deprecated dotted names mapping to the same option - // in the same file. - { - OptionsParserTester parser; - moe::Environment environment; - std::map<std::string, std::string> env_map; - - std::stringstream ss; - ss << deprecatedDottedNames[0] << ": 6" << std::endl - << deprecatedDottedNames[1] << ": 7"; - parser.setConfig("config.yaml", ss.str()); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - } - - TEST(YAMLConfigFile, ListBrackets) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.yaml", "multival: [ \"val1\", \"val2\" ]"); - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("multival"), &value)); - std::vector<std::string> multival; - std::vector<std::string>::iterator multivalit; - ASSERT_OK(value.get(&multival)); - multivalit = multival.begin(); - ASSERT_EQUALS(*multivalit, "val1"); - multivalit++; - ASSERT_EQUALS(*multivalit, "val2"); + ASSERT_FALSE(environment.count(moe::Key(deprecatedDottedNames[0]))); + ASSERT_FALSE(environment.count(moe::Key(deprecatedDottedNames[1]))); } - TEST(YAMLConfigFile, ListDashes) { + // It is an error to have multiple deprecated dotted names mapping to the same option + // in the same file. + { OptionsParserTester parser; moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); std::map<std::string, std::string> env_map; - parser.setConfig("config.yaml", "multival:\n - \"val1\"\n - \"val2\""); + std::stringstream ss; + ss << deprecatedDottedNames[0] << ": 6" << std::endl + << deprecatedDottedNames[1] << ": 7"; + parser.setConfig("config.yaml", ss.str()); - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("multival"), &value)); - std::vector<std::string> multival; - std::vector<std::string>::iterator multivalit; - ASSERT_OK(value.get(&multival)); - multivalit = multival.begin(); - ASSERT_EQUALS(*multivalit, "val1"); - multivalit++; - ASSERT_EQUALS(*multivalit, "val2"); - } - - TEST(YAMLConfigFile, DefaultValueOverride) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port").setDefault(moe::Value(5)); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.yaml", "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(YAMLConfigFile, Comments) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - testOpts.addOptionChaining("host", "host", moe::String, "Host"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.yaml", - "# comment on port\nport: 5\n" - "# comment on host\nhost: localhost\n"); - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("port"), &value)); - int port; - ASSERT_OK(value.get(&port)); - ASSERT_EQUALS(port, 5); - ASSERT_OK(environment.get(moe::Key("host"), &value)); - std::string host; - ASSERT_OK(value.get(&host)); - ASSERT_EQUALS(host, "localhost"); - } - - TEST(YAMLConfigFile, EmptyKey) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("port", "port", moe::Int, "Port"); - - std::vector<std::string> argv; - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - std::map<std::string, std::string> env_map; - - parser.setConfig("config.yaml", ":"); - - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - } - - TEST(YAMLConfigFile, StringVector) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); - - 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", "multival : [ \"val1\", \"val2\" ]"); - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("multival"), &value)); - std::vector<std::string> multival; - std::vector<std::string>::iterator multivalit; - ASSERT_OK(value.get(&multival)); - multivalit = multival.begin(); - ASSERT_EQUALS(*multivalit, "val1"); - multivalit++; - ASSERT_EQUALS(*multivalit, "val2"); - } - - TEST(YAMLConfigFile, StringMap) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); - - 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", - // NOTE: Indentation is used to determine whether an option is in a sub - // category, so the spaces after the newlines before key2 and key3 is - // significant - "multival : \n key1 : \"value1\"\n key2 : \"value2\"\n key3 : \"\""); - - moe::Value value; - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - ASSERT_OK(environment.get(moe::Key("multival"), &value)); - std::map<std::string, std::string> multival; - std::map<std::string, std::string>::iterator multivalit; - ASSERT_OK(value.get(&multival)); - multivalit = multival.begin(); - ASSERT_EQUALS(multivalit->first, "key1"); - ASSERT_EQUALS(multivalit->second, "value1"); - multivalit++; - ASSERT_EQUALS(multivalit->first, "key2"); - ASSERT_EQUALS(multivalit->second, "value2"); - multivalit++; - ASSERT_EQUALS(multivalit->first, "key3"); - ASSERT_EQUALS(multivalit->second, ""); - } - - TEST(YAMLConfigFile, StringMapDuplicateKey) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); - - 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", - // NOTE: Indentation is used to determine whether an option is in a sub - // category, so the spaces after the newlines before key2 and key3 is - // significant - "multival : \n key1 : \"value1\"\n key1 : \"value2\""); - - moe::Value value; ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); } - - TEST(OptionCount, Basic) { - OptionsParserTester parser; - moe::Environment environment; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("basic", "basic", moe::String, "Basic Option"); - testOpts.addOptionChaining("hidden", "hidden", moe::String, "Hidden Option").hidden(); - - moe::OptionSection subSection("Section Name"); - subSection.addOptionChaining("port", "port", moe::Int, "Port") - .setSources(moe::SourceYAMLConfig); - testOpts.addSection(subSection); - - int numOptions; - ASSERT_OK(testOpts.countOptions(&numOptions, true /*visibleOnly*/, moe::SourceCommandLine)); - ASSERT_EQUALS(numOptions, 1); - } - - TEST(NumericalBaseParsing, CommandLine) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("doubleVal", "doubleVal", moe::Double, "DoubleVal"); - testOpts.addOptionChaining("intVal", "intVal", moe::Int, "IntVal"); - testOpts.addOptionChaining("longVal", "longVal", moe::Long, "LongVal"); - testOpts.addOptionChaining("unsignedLongLongVal", "unsignedLongLongVal", - moe::UnsignedLongLong, "UnsignedLongLongVal"); - testOpts.addOptionChaining("unsignedVal", "unsignedVal", moe::Unsigned, "UnsignedVal"); - - // Bad values - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--doubleVal"); - argv.push_back("monkeys"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--intVal"); - argv.push_back("monkeys"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--longVal"); - argv.push_back("monkeys"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--unsignedLongLongVal"); - argv.push_back("monkeys"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--unsignedVal"); - argv.push_back("monkeys"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // Decimal values - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--doubleVal"); - argv.push_back("16.1"); - argv.push_back("--intVal"); - argv.push_back("16"); - argv.push_back("--longVal"); - argv.push_back("16"); - argv.push_back("--unsignedLongLongVal"); - argv.push_back("16"); - argv.push_back("--unsignedVal"); - argv.push_back("16"); - environment = moe::Environment(); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - - double doubleVal; - int intVal; - long longVal; - unsigned long long unsignedLongLongVal; - unsigned unsignedVal; - - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, 16.1); - - ASSERT_OK(environment.get(moe::Key("intVal"), &value)); - ASSERT_OK(value.get(&intVal)); - ASSERT_EQUALS(intVal, 16); - - ASSERT_OK(environment.get(moe::Key("longVal"), &value)); - ASSERT_OK(value.get(&longVal)); - ASSERT_EQUALS(longVal, 16); - - ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); - ASSERT_OK(value.get(&unsignedLongLongVal)); - ASSERT_EQUALS(unsignedLongLongVal, 16ULL); - - ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); - ASSERT_OK(value.get(&unsignedVal)); - ASSERT_EQUALS(unsignedVal, 16U); - - // Octal values - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--doubleVal"); - argv.push_back("020.1"); - argv.push_back("--intVal"); - argv.push_back("020"); - argv.push_back("--longVal"); - argv.push_back("020"); - argv.push_back("--unsignedLongLongVal"); - argv.push_back("020"); - argv.push_back("--unsignedVal"); - argv.push_back("020"); - environment = moe::Environment(); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, 020.1); - - ASSERT_OK(environment.get(moe::Key("intVal"), &value)); - ASSERT_OK(value.get(&intVal)); - ASSERT_EQUALS(intVal, 020); - - ASSERT_OK(environment.get(moe::Key("longVal"), &value)); - ASSERT_OK(value.get(&longVal)); - ASSERT_EQUALS(longVal, 020); - - ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); - ASSERT_OK(value.get(&unsignedLongLongVal)); - ASSERT_EQUALS(unsignedLongLongVal, 020ULL); - - ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); - ASSERT_OK(value.get(&unsignedVal)); - ASSERT_EQUALS(unsignedVal, 020U); - - // Hex values - argv = std::vector<std::string>(); - argv.push_back("binaryname"); +} + +TEST(YAMLConfigFile, ListBrackets) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", "multival: [ \"val1\", \"val2\" ]"); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("multival"), &value)); + std::vector<std::string> multival; + std::vector<std::string>::iterator multivalit; + ASSERT_OK(value.get(&multival)); + multivalit = multival.begin(); + ASSERT_EQUALS(*multivalit, "val1"); + multivalit++; + ASSERT_EQUALS(*multivalit, "val2"); +} + +TEST(YAMLConfigFile, ListDashes) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", "multival:\n - \"val1\"\n - \"val2\""); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("multival"), &value)); + std::vector<std::string> multival; + std::vector<std::string>::iterator multivalit; + ASSERT_OK(value.get(&multival)); + multivalit = multival.begin(); + ASSERT_EQUALS(*multivalit, "val1"); + multivalit++; + ASSERT_EQUALS(*multivalit, "val2"); +} + +TEST(YAMLConfigFile, DefaultValueOverride) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port").setDefault(moe::Value(5)); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", "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(YAMLConfigFile, Comments) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + testOpts.addOptionChaining("host", "host", moe::String, "Host"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", + "# comment on port\nport: 5\n" + "# comment on host\nhost: localhost\n"); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); + ASSERT_OK(environment.get(moe::Key("host"), &value)); + std::string host; + ASSERT_OK(value.get(&host)); + ASSERT_EQUALS(host, "localhost"); +} + +TEST(YAMLConfigFile, EmptyKey) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port"); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.yaml", ":"); + + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(YAMLConfigFile, StringVector) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("multival", "multival", moe::StringVector, "Multiple Values"); + + 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", "multival : [ \"val1\", \"val2\" ]"); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("multival"), &value)); + std::vector<std::string> multival; + std::vector<std::string>::iterator multivalit; + ASSERT_OK(value.get(&multival)); + multivalit = multival.begin(); + ASSERT_EQUALS(*multivalit, "val1"); + multivalit++; + ASSERT_EQUALS(*multivalit, "val2"); +} + +TEST(YAMLConfigFile, StringMap) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); + + 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", + // NOTE: Indentation is used to determine whether an option is in a sub + // category, so the spaces after the newlines before key2 and key3 is + // significant + "multival : \n key1 : \"value1\"\n key2 : \"value2\"\n key3 : \"\""); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("multival"), &value)); + std::map<std::string, std::string> multival; + std::map<std::string, std::string>::iterator multivalit; + ASSERT_OK(value.get(&multival)); + multivalit = multival.begin(); + ASSERT_EQUALS(multivalit->first, "key1"); + ASSERT_EQUALS(multivalit->second, "value1"); + multivalit++; + ASSERT_EQUALS(multivalit->first, "key2"); + ASSERT_EQUALS(multivalit->second, "value2"); + multivalit++; + ASSERT_EQUALS(multivalit->first, "key3"); + ASSERT_EQUALS(multivalit->second, ""); +} + +TEST(YAMLConfigFile, StringMapDuplicateKey) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("multival", "multival", moe::StringMap, "Multiple Values"); + + 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", + // NOTE: Indentation is used to determine whether an option is in a sub + // category, so the spaces after the newlines before key2 and key3 is + // significant + "multival : \n key1 : \"value1\"\n key1 : \"value2\""); + + moe::Value value; + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); +} + +TEST(OptionCount, Basic) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("basic", "basic", moe::String, "Basic Option"); + testOpts.addOptionChaining("hidden", "hidden", moe::String, "Hidden Option").hidden(); + + moe::OptionSection subSection("Section Name"); + subSection.addOptionChaining("port", "port", moe::Int, "Port") + .setSources(moe::SourceYAMLConfig); + testOpts.addSection(subSection); + + int numOptions; + ASSERT_OK(testOpts.countOptions(&numOptions, true /*visibleOnly*/, moe::SourceCommandLine)); + ASSERT_EQUALS(numOptions, 1); +} + +TEST(NumericalBaseParsing, CommandLine) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("doubleVal", "doubleVal", moe::Double, "DoubleVal"); + testOpts.addOptionChaining("intVal", "intVal", moe::Int, "IntVal"); + testOpts.addOptionChaining("longVal", "longVal", moe::Long, "LongVal"); + testOpts.addOptionChaining( + "unsignedLongLongVal", "unsignedLongLongVal", moe::UnsignedLongLong, "UnsignedLongLongVal"); + testOpts.addOptionChaining("unsignedVal", "unsignedVal", moe::Unsigned, "UnsignedVal"); + + // Bad values + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--doubleVal"); + argv.push_back("monkeys"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--intVal"); + argv.push_back("monkeys"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--longVal"); + argv.push_back("monkeys"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--unsignedLongLongVal"); + argv.push_back("monkeys"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--unsignedVal"); + argv.push_back("monkeys"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // Decimal values + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--doubleVal"); + argv.push_back("16.1"); + argv.push_back("--intVal"); + argv.push_back("16"); + argv.push_back("--longVal"); + argv.push_back("16"); + argv.push_back("--unsignedLongLongVal"); + argv.push_back("16"); + argv.push_back("--unsignedVal"); + argv.push_back("16"); + environment = moe::Environment(); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + + double doubleVal; + int intVal; + long longVal; + unsigned long long unsignedLongLongVal; + unsigned unsignedVal; + + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, 16.1); + + ASSERT_OK(environment.get(moe::Key("intVal"), &value)); + ASSERT_OK(value.get(&intVal)); + ASSERT_EQUALS(intVal, 16); + + ASSERT_OK(environment.get(moe::Key("longVal"), &value)); + ASSERT_OK(value.get(&longVal)); + ASSERT_EQUALS(longVal, 16); + + ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); + ASSERT_OK(value.get(&unsignedLongLongVal)); + ASSERT_EQUALS(unsignedLongLongVal, 16ULL); + + ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); + ASSERT_OK(value.get(&unsignedVal)); + ASSERT_EQUALS(unsignedVal, 16U); + + // Octal values + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--doubleVal"); + argv.push_back("020.1"); + argv.push_back("--intVal"); + argv.push_back("020"); + argv.push_back("--longVal"); + argv.push_back("020"); + argv.push_back("--unsignedLongLongVal"); + argv.push_back("020"); + argv.push_back("--unsignedVal"); + argv.push_back("020"); + environment = moe::Environment(); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, 020.1); + + ASSERT_OK(environment.get(moe::Key("intVal"), &value)); + ASSERT_OK(value.get(&intVal)); + ASSERT_EQUALS(intVal, 020); + + ASSERT_OK(environment.get(moe::Key("longVal"), &value)); + ASSERT_OK(value.get(&longVal)); + ASSERT_EQUALS(longVal, 020); + + ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); + ASSERT_OK(value.get(&unsignedLongLongVal)); + ASSERT_EQUALS(unsignedLongLongVal, 020ULL); + + ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); + ASSERT_OK(value.get(&unsignedVal)); + ASSERT_EQUALS(unsignedVal, 020U); + + // Hex values + argv = std::vector<std::string>(); + argv.push_back("binaryname"); #if !(defined(_WIN32) || defined(__sunos__)) - // Hex doubles are not parseable by the Windows SDK libc or the Solaris libc in the mode we - // build, so we cannot read hex doubles from the command line on those platforms. - // See SERVER-14131. + // Hex doubles are not parseable by the Windows SDK libc or the Solaris libc in the mode we + // build, so we cannot read hex doubles from the command line on those platforms. + // See SERVER-14131. - argv.push_back("--doubleVal"); - argv.push_back("0x10.1"); + argv.push_back("--doubleVal"); + argv.push_back("0x10.1"); #endif - argv.push_back("--intVal"); - argv.push_back("0x10"); - argv.push_back("--longVal"); - argv.push_back("0x10"); - argv.push_back("--unsignedLongLongVal"); - argv.push_back("0x10"); - argv.push_back("--unsignedVal"); - argv.push_back("0x10"); - environment = moe::Environment(); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + argv.push_back("--intVal"); + argv.push_back("0x10"); + argv.push_back("--longVal"); + argv.push_back("0x10"); + argv.push_back("--unsignedLongLongVal"); + argv.push_back("0x10"); + argv.push_back("--unsignedVal"); + argv.push_back("0x10"); + environment = moe::Environment(); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); #if !(defined(_WIN32) || defined(__sunos__)) - // See SERVER-14131. - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, 0x10.1p0); + // See SERVER-14131. + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, 0x10.1p0); #endif - ASSERT_OK(environment.get(moe::Key("intVal"), &value)); - ASSERT_OK(value.get(&intVal)); - ASSERT_EQUALS(intVal, 0x10); - - ASSERT_OK(environment.get(moe::Key("longVal"), &value)); - ASSERT_OK(value.get(&longVal)); - ASSERT_EQUALS(longVal, 0x10); - - ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); - ASSERT_OK(value.get(&unsignedLongLongVal)); - ASSERT_EQUALS(unsignedLongLongVal, 0x10ULL); - - ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); - ASSERT_OK(value.get(&unsignedVal)); - ASSERT_EQUALS(unsignedVal, 0x10U); - } - - TEST(NumericalBaseParsing, INIConfigFile) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("doubleVal", "doubleVal", moe::Double, "DoubleVal"); - testOpts.addOptionChaining("intVal", "intVal", moe::Int, "IntVal"); - testOpts.addOptionChaining("longVal", "longVal", moe::Long, "LongVal"); - testOpts.addOptionChaining("unsignedLongLongVal", "unsignedLongLongVal", - moe::UnsignedLongLong, "UnsignedLongLongVal"); - testOpts.addOptionChaining("unsignedVal", "unsignedVal", moe::Unsigned, "UnsignedVal"); - - // Bad values - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); - - parser.setConfig("config.ini", "doubleVal=monkeys"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - parser.setConfig("config.ini", "intVal=monkeys"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - parser.setConfig("config.ini", "longVal=monkeys"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - parser.setConfig("config.ini", "unsignedLongLongVal=monkeys"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - parser.setConfig("config.ini", "unsignedVal=monkeys"); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // Decimal values - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); - parser.setConfig("config.ini", "doubleVal=16.1\nintVal=16\nlongVal=16\n" - "unsignedLongLongVal=16\nunsignedVal=16\n"); - environment = moe::Environment(); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - - double doubleVal; - int intVal; - long longVal; - unsigned long long unsignedLongLongVal; - unsigned unsignedVal; - - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, 16.1); - - ASSERT_OK(environment.get(moe::Key("intVal"), &value)); - ASSERT_OK(value.get(&intVal)); - ASSERT_EQUALS(intVal, 16); - - ASSERT_OK(environment.get(moe::Key("longVal"), &value)); - ASSERT_OK(value.get(&longVal)); - ASSERT_EQUALS(longVal, 16); - - ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); - ASSERT_OK(value.get(&unsignedLongLongVal)); - ASSERT_EQUALS(unsignedLongLongVal, 16ULL); - - ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); - ASSERT_OK(value.get(&unsignedVal)); - ASSERT_EQUALS(unsignedVal, 16U); - - // Octal values - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); - parser.setConfig("config.ini", "doubleVal=020.1\nintVal=020\nlongVal=020\n" - "unsignedLongLongVal=020\nunsignedVal=020\n"); - environment = moe::Environment(); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, 020.1); - - ASSERT_OK(environment.get(moe::Key("intVal"), &value)); - ASSERT_OK(value.get(&intVal)); - ASSERT_EQUALS(intVal, 020); - - ASSERT_OK(environment.get(moe::Key("longVal"), &value)); - ASSERT_OK(value.get(&longVal)); - ASSERT_EQUALS(longVal, 020); - - ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); - ASSERT_OK(value.get(&unsignedLongLongVal)); - ASSERT_EQUALS(unsignedLongLongVal, 020ULL); - - ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); - ASSERT_OK(value.get(&unsignedVal)); - ASSERT_EQUALS(unsignedVal, 020U); - - // Hex values - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.ini"); + ASSERT_OK(environment.get(moe::Key("intVal"), &value)); + ASSERT_OK(value.get(&intVal)); + ASSERT_EQUALS(intVal, 0x10); + + ASSERT_OK(environment.get(moe::Key("longVal"), &value)); + ASSERT_OK(value.get(&longVal)); + ASSERT_EQUALS(longVal, 0x10); + + ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); + ASSERT_OK(value.get(&unsignedLongLongVal)); + ASSERT_EQUALS(unsignedLongLongVal, 0x10ULL); + + ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); + ASSERT_OK(value.get(&unsignedVal)); + ASSERT_EQUALS(unsignedVal, 0x10U); +} + +TEST(NumericalBaseParsing, INIConfigFile) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("doubleVal", "doubleVal", moe::Double, "DoubleVal"); + testOpts.addOptionChaining("intVal", "intVal", moe::Int, "IntVal"); + testOpts.addOptionChaining("longVal", "longVal", moe::Long, "LongVal"); + testOpts.addOptionChaining( + "unsignedLongLongVal", "unsignedLongLongVal", moe::UnsignedLongLong, "UnsignedLongLongVal"); + testOpts.addOptionChaining("unsignedVal", "unsignedVal", moe::Unsigned, "UnsignedVal"); + + // Bad values + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); + + parser.setConfig("config.ini", "doubleVal=monkeys"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + parser.setConfig("config.ini", "intVal=monkeys"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + parser.setConfig("config.ini", "longVal=monkeys"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + parser.setConfig("config.ini", "unsignedLongLongVal=monkeys"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + parser.setConfig("config.ini", "unsignedVal=monkeys"); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // Decimal values + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); + parser.setConfig("config.ini", + "doubleVal=16.1\nintVal=16\nlongVal=16\n" + "unsignedLongLongVal=16\nunsignedVal=16\n"); + environment = moe::Environment(); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + + double doubleVal; + int intVal; + long longVal; + unsigned long long unsignedLongLongVal; + unsigned unsignedVal; + + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, 16.1); + + ASSERT_OK(environment.get(moe::Key("intVal"), &value)); + ASSERT_OK(value.get(&intVal)); + ASSERT_EQUALS(intVal, 16); + + ASSERT_OK(environment.get(moe::Key("longVal"), &value)); + ASSERT_OK(value.get(&longVal)); + ASSERT_EQUALS(longVal, 16); + + ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); + ASSERT_OK(value.get(&unsignedLongLongVal)); + ASSERT_EQUALS(unsignedLongLongVal, 16ULL); + + ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); + ASSERT_OK(value.get(&unsignedVal)); + ASSERT_EQUALS(unsignedVal, 16U); + + // Octal values + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); + parser.setConfig("config.ini", + "doubleVal=020.1\nintVal=020\nlongVal=020\n" + "unsignedLongLongVal=020\nunsignedVal=020\n"); + environment = moe::Environment(); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, 020.1); + + ASSERT_OK(environment.get(moe::Key("intVal"), &value)); + ASSERT_OK(value.get(&intVal)); + ASSERT_EQUALS(intVal, 020); + + ASSERT_OK(environment.get(moe::Key("longVal"), &value)); + ASSERT_OK(value.get(&longVal)); + ASSERT_EQUALS(longVal, 020); + + ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); + ASSERT_OK(value.get(&unsignedLongLongVal)); + ASSERT_EQUALS(unsignedLongLongVal, 020ULL); + + ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); + ASSERT_OK(value.get(&unsignedVal)); + ASSERT_EQUALS(unsignedVal, 020U); + + // Hex values + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.ini"); #if !(defined(_WIN32) || defined(__sunos__)) - // Hex doubles are not parseable by the Windows SDK libc or the Solaris libc in the mode we - // build, so we cannot read hex doubles from a config file on those platforms. - // See SERVER-14131. + // Hex doubles are not parseable by the Windows SDK libc or the Solaris libc in the mode we + // build, so we cannot read hex doubles from a config file on those platforms. + // See SERVER-14131. - parser.setConfig("config.ini", "doubleVal=0x10.1\nintVal=0x10\nlongVal=0x10\n" - "unsignedLongLongVal=0x10\nunsignedVal=0x10\n"); + parser.setConfig("config.ini", + "doubleVal=0x10.1\nintVal=0x10\nlongVal=0x10\n" + "unsignedLongLongVal=0x10\nunsignedVal=0x10\n"); #else - parser.setConfig("config.ini", "intVal=0x10\nlongVal=0x10\n" - "unsignedLongLongVal=0x10\nunsignedVal=0x10\n"); + parser.setConfig("config.ini", + "intVal=0x10\nlongVal=0x10\n" + "unsignedLongLongVal=0x10\nunsignedVal=0x10\n"); #endif - environment = moe::Environment(); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); #if !(defined(_WIN32) || defined(__sunos__)) - // See SERVER-14131. - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, 0x10.1p0); + // See SERVER-14131. + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, 0x10.1p0); #endif - ASSERT_OK(environment.get(moe::Key("intVal"), &value)); - ASSERT_OK(value.get(&intVal)); - ASSERT_EQUALS(intVal, 0x10); - - ASSERT_OK(environment.get(moe::Key("longVal"), &value)); - ASSERT_OK(value.get(&longVal)); - ASSERT_EQUALS(longVal, 0x10); - - ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); - ASSERT_OK(value.get(&unsignedLongLongVal)); - ASSERT_EQUALS(unsignedLongLongVal, 0x10ULL); - - ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); - ASSERT_OK(value.get(&unsignedVal)); - ASSERT_EQUALS(unsignedVal, 0x10U); - } - - TEST(NumericalBaseParsing, YAMLConfigFile) { - OptionsParserTester parser; - moe::Environment environment; - moe::Value value; - std::vector<std::string> argv; - std::map<std::string, std::string> env_map; - - moe::OptionSection testOpts; - testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); - testOpts.addOptionChaining("doubleVal", "doubleVal", moe::Double, "DoubleVal"); - testOpts.addOptionChaining("intVal", "intVal", moe::Int, "IntVal"); - testOpts.addOptionChaining("longVal", "longVal", moe::Long, "LongVal"); - testOpts.addOptionChaining("unsignedLongLongVal", "unsignedLongLongVal", - moe::UnsignedLongLong, "UnsignedLongLongVal"); - testOpts.addOptionChaining("unsignedVal", "unsignedVal", moe::Unsigned, "UnsignedVal"); - - // Bad values - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - - parser.setConfig("config.yaml", "doubleVal: \"monkeys\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - parser.setConfig("config.yaml", "intVal: \"monkeys\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - parser.setConfig("config.yaml", "longVal: \"monkeys\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - parser.setConfig("config.yaml", "unsignedLongLongVal: \"monkeys\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - parser.setConfig("config.yaml", "unsignedVal: \"monkeys\""); - ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); - - // Decimal values - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - parser.setConfig("config.yaml", "doubleVal: 16.1\nintVal: 16\nlongVal: 16\n" - "unsignedLongLongVal: 16\nunsignedVal: 16\n"); - environment = moe::Environment(); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - - double doubleVal; - int intVal; - long longVal; - unsigned long long unsignedLongLongVal; - unsigned unsignedVal; - - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, 16.1); - - ASSERT_OK(environment.get(moe::Key("intVal"), &value)); - ASSERT_OK(value.get(&intVal)); - ASSERT_EQUALS(intVal, 16); - - ASSERT_OK(environment.get(moe::Key("longVal"), &value)); - ASSERT_OK(value.get(&longVal)); - ASSERT_EQUALS(longVal, 16); - - ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); - ASSERT_OK(value.get(&unsignedLongLongVal)); - ASSERT_EQUALS(unsignedLongLongVal, 16ULL); - - ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); - ASSERT_OK(value.get(&unsignedVal)); - ASSERT_EQUALS(unsignedVal, 16U); - - // Octal values - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); - parser.setConfig("config.yaml", "doubleVal: 020.1\nintVal: 020\nlongVal: 020\n" - "unsignedLongLongVal: 020\nunsignedVal: 020\n"); - environment = moe::Environment(); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); - - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, 020.1); - - ASSERT_OK(environment.get(moe::Key("intVal"), &value)); - ASSERT_OK(value.get(&intVal)); - ASSERT_EQUALS(intVal, 020); - - ASSERT_OK(environment.get(moe::Key("longVal"), &value)); - ASSERT_OK(value.get(&longVal)); - ASSERT_EQUALS(longVal, 020); - - ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); - ASSERT_OK(value.get(&unsignedLongLongVal)); - ASSERT_EQUALS(unsignedLongLongVal, 020ULL); - - ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); - ASSERT_OK(value.get(&unsignedVal)); - ASSERT_EQUALS(unsignedVal, 020U); - - // Hex values - argv = std::vector<std::string>(); - argv.push_back("binaryname"); - argv.push_back("--config"); - argv.push_back("config.yaml"); + ASSERT_OK(environment.get(moe::Key("intVal"), &value)); + ASSERT_OK(value.get(&intVal)); + ASSERT_EQUALS(intVal, 0x10); + + ASSERT_OK(environment.get(moe::Key("longVal"), &value)); + ASSERT_OK(value.get(&longVal)); + ASSERT_EQUALS(longVal, 0x10); + + ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); + ASSERT_OK(value.get(&unsignedLongLongVal)); + ASSERT_EQUALS(unsignedLongLongVal, 0x10ULL); + + ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); + ASSERT_OK(value.get(&unsignedVal)); + ASSERT_EQUALS(unsignedVal, 0x10U); +} + +TEST(NumericalBaseParsing, YAMLConfigFile) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("doubleVal", "doubleVal", moe::Double, "DoubleVal"); + testOpts.addOptionChaining("intVal", "intVal", moe::Int, "IntVal"); + testOpts.addOptionChaining("longVal", "longVal", moe::Long, "LongVal"); + testOpts.addOptionChaining( + "unsignedLongLongVal", "unsignedLongLongVal", moe::UnsignedLongLong, "UnsignedLongLongVal"); + testOpts.addOptionChaining("unsignedVal", "unsignedVal", moe::Unsigned, "UnsignedVal"); + + // Bad values + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + + parser.setConfig("config.yaml", "doubleVal: \"monkeys\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + parser.setConfig("config.yaml", "intVal: \"monkeys\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + parser.setConfig("config.yaml", "longVal: \"monkeys\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + parser.setConfig("config.yaml", "unsignedLongLongVal: \"monkeys\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + parser.setConfig("config.yaml", "unsignedVal: \"monkeys\""); + ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); + + // Decimal values + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + parser.setConfig("config.yaml", + "doubleVal: 16.1\nintVal: 16\nlongVal: 16\n" + "unsignedLongLongVal: 16\nunsignedVal: 16\n"); + environment = moe::Environment(); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + + double doubleVal; + int intVal; + long longVal; + unsigned long long unsignedLongLongVal; + unsigned unsignedVal; + + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, 16.1); + + ASSERT_OK(environment.get(moe::Key("intVal"), &value)); + ASSERT_OK(value.get(&intVal)); + ASSERT_EQUALS(intVal, 16); + + ASSERT_OK(environment.get(moe::Key("longVal"), &value)); + ASSERT_OK(value.get(&longVal)); + ASSERT_EQUALS(longVal, 16); + + ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); + ASSERT_OK(value.get(&unsignedLongLongVal)); + ASSERT_EQUALS(unsignedLongLongVal, 16ULL); + + ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); + ASSERT_OK(value.get(&unsignedVal)); + ASSERT_EQUALS(unsignedVal, 16U); + + // Octal values + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); + parser.setConfig("config.yaml", + "doubleVal: 020.1\nintVal: 020\nlongVal: 020\n" + "unsignedLongLongVal: 020\nunsignedVal: 020\n"); + environment = moe::Environment(); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, 020.1); + + ASSERT_OK(environment.get(moe::Key("intVal"), &value)); + ASSERT_OK(value.get(&intVal)); + ASSERT_EQUALS(intVal, 020); + + ASSERT_OK(environment.get(moe::Key("longVal"), &value)); + ASSERT_OK(value.get(&longVal)); + ASSERT_EQUALS(longVal, 020); + + ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); + ASSERT_OK(value.get(&unsignedLongLongVal)); + ASSERT_EQUALS(unsignedLongLongVal, 020ULL); + + ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); + ASSERT_OK(value.get(&unsignedVal)); + ASSERT_EQUALS(unsignedVal, 020U); + + // Hex values + argv = std::vector<std::string>(); + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.yaml"); #if !(defined(_WIN32) || defined(__sunos__)) - // Hex doubles are not parseable by the Windows SDK libc or the Solaris libc in the mode we - // build, so we cannot read hex doubles from a config file on those platforms. - // See SERVER-14131. + // Hex doubles are not parseable by the Windows SDK libc or the Solaris libc in the mode we + // build, so we cannot read hex doubles from a config file on those platforms. + // See SERVER-14131. - parser.setConfig("config.yaml", "doubleVal: 0x10.1\nintVal: 0x10\nlongVal: 0x10\n" - "unsignedLongLongVal: 0x10\nunsignedVal: 0x10\n"); + parser.setConfig("config.yaml", + "doubleVal: 0x10.1\nintVal: 0x10\nlongVal: 0x10\n" + "unsignedLongLongVal: 0x10\nunsignedVal: 0x10\n"); #else - parser.setConfig("config.yaml", "intVal: 0x10\nlongVal: 0x10\n" - "unsignedLongLongVal: 0x10\nunsignedVal: 0x10\n"); + parser.setConfig("config.yaml", + "intVal: 0x10\nlongVal: 0x10\n" + "unsignedLongLongVal: 0x10\nunsignedVal: 0x10\n"); #endif - environment = moe::Environment(); - ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + environment = moe::Environment(); + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); #if !(defined(_WIN32) || defined(__sunos__)) - // See SERVER-14131. - ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); - ASSERT_OK(value.get(&doubleVal)); - ASSERT_EQUALS(doubleVal, 0x10.1p0); + // See SERVER-14131. + ASSERT_OK(environment.get(moe::Key("doubleVal"), &value)); + ASSERT_OK(value.get(&doubleVal)); + ASSERT_EQUALS(doubleVal, 0x10.1p0); #endif - ASSERT_OK(environment.get(moe::Key("intVal"), &value)); - ASSERT_OK(value.get(&intVal)); - ASSERT_EQUALS(intVal, 0x10); + ASSERT_OK(environment.get(moe::Key("intVal"), &value)); + ASSERT_OK(value.get(&intVal)); + ASSERT_EQUALS(intVal, 0x10); - ASSERT_OK(environment.get(moe::Key("longVal"), &value)); - ASSERT_OK(value.get(&longVal)); - ASSERT_EQUALS(longVal, 0x10); + ASSERT_OK(environment.get(moe::Key("longVal"), &value)); + ASSERT_OK(value.get(&longVal)); + ASSERT_EQUALS(longVal, 0x10); - ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); - ASSERT_OK(value.get(&unsignedLongLongVal)); - ASSERT_EQUALS(unsignedLongLongVal, 0x10ULL); + ASSERT_OK(environment.get(moe::Key("unsignedLongLongVal"), &value)); + ASSERT_OK(value.get(&unsignedLongLongVal)); + ASSERT_EQUALS(unsignedLongLongVal, 0x10ULL); - ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); - ASSERT_OK(value.get(&unsignedVal)); - ASSERT_EQUALS(unsignedVal, 0x10U); - } + ASSERT_OK(environment.get(moe::Key("unsignedVal"), &value)); + ASSERT_OK(value.get(&unsignedVal)); + ASSERT_EQUALS(unsignedVal, 0x10U); +} -} // unnamed namespace +} // unnamed namespace diff --git a/src/mongo/util/options_parser/startup_option_init.cpp b/src/mongo/util/options_parser/startup_option_init.cpp index 21dfad053db..0ff85fb9f49 100644 --- a/src/mongo/util/options_parser/startup_option_init.cpp +++ b/src/mongo/util/options_parser/startup_option_init.cpp @@ -34,38 +34,49 @@ /* Groups for all of option handling */ MONGO_INITIALIZER_GROUP(BeginStartupOptionHandling, - ("GlobalLogManager", "ValidateLocale"), ("EndStartupOptionHandling")) + ("GlobalLogManager", "ValidateLocale"), + ("EndStartupOptionHandling")) /* Groups for option registration */ MONGO_INITIALIZER_GROUP(BeginStartupOptionRegistration, - ("BeginStartupOptionHandling"), ("EndStartupOptionRegistration")) + ("BeginStartupOptionHandling"), + ("EndStartupOptionRegistration")) /* Groups for general option registration (useful for controlling the order in which options are * registered for modules, which affects the order in which they are printed in help output) */ MONGO_INITIALIZER_GROUP(BeginGeneralStartupOptionRegistration, - ("BeginStartupOptionRegistration"), ("EndGeneralStartupOptionRegistration")) + ("BeginStartupOptionRegistration"), + ("EndGeneralStartupOptionRegistration")) MONGO_INITIALIZER_GROUP(EndGeneralStartupOptionRegistration, - ("BeginGeneralStartupOptionRegistration"), ("EndStartupOptionRegistration")) + ("BeginGeneralStartupOptionRegistration"), + ("EndStartupOptionRegistration")) MONGO_INITIALIZER_GROUP(EndStartupOptionRegistration, - ("BeginStartupOptionRegistration"), ("BeginStartupOptionParsing")) + ("BeginStartupOptionRegistration"), + ("BeginStartupOptionParsing")) /* Groups for option parsing */ MONGO_INITIALIZER_GROUP(BeginStartupOptionParsing, - ("EndStartupOptionRegistration"), ("EndStartupOptionParsing")) + ("EndStartupOptionRegistration"), + ("EndStartupOptionParsing")) MONGO_INITIALIZER_GROUP(EndStartupOptionParsing, - ("BeginStartupOptionParsing"), ("BeginStartupOptionValidation")) + ("BeginStartupOptionParsing"), + ("BeginStartupOptionValidation")) /* Groups for option validation */ MONGO_INITIALIZER_GROUP(BeginStartupOptionValidation, - ("EndStartupOptionParsing"), ("EndStartupOptionValidation")) + ("EndStartupOptionParsing"), + ("EndStartupOptionValidation")) MONGO_INITIALIZER_GROUP(EndStartupOptionValidation, - ("BeginStartupOptionValidation"), ("BeginStartupOptionStorage")) + ("BeginStartupOptionValidation"), + ("BeginStartupOptionStorage")) /* Groups for option storage */ MONGO_INITIALIZER_GROUP(BeginStartupOptionStorage, - ("EndStartupOptionValidation"), ("EndStartupOptionStorage")) + ("EndStartupOptionValidation"), + ("EndStartupOptionStorage")) MONGO_INITIALIZER_GROUP(EndStartupOptionStorage, - ("BeginStartupOptionStorage"), ("EndStartupOptionHandling")) + ("BeginStartupOptionStorage"), + ("EndStartupOptionHandling")) MONGO_INITIALIZER_GROUP(EndStartupOptionHandling, ("BeginStartupOptionHandling"), ("default")) diff --git a/src/mongo/util/options_parser/startup_option_init.h b/src/mongo/util/options_parser/startup_option_init.h index ce8671e0e05..7133611849e 100644 --- a/src/mongo/util/options_parser/startup_option_init.h +++ b/src/mongo/util/options_parser/startup_option_init.h @@ -59,8 +59,8 @@ * return Status::OK(); * } */ -#define MONGO_GENERAL_STARTUP_OPTIONS_REGISTER(fname) \ - MONGO_INITIALIZER_GENERAL(fname##_Register, \ +#define MONGO_GENERAL_STARTUP_OPTIONS_REGISTER(fname) \ + MONGO_INITIALIZER_GENERAL(fname##_Register, \ ("BeginGeneralStartupOptionRegistration"), \ ("EndGeneralStartupOptionRegistration")) @@ -74,8 +74,8 @@ * return Status::OK(); * } */ -#define MONGO_MODULE_STARTUP_OPTIONS_REGISTER(fname) \ - MONGO_INITIALIZER_GENERAL(fname##_Register, \ +#define MONGO_MODULE_STARTUP_OPTIONS_REGISTER(fname) \ + MONGO_INITIALIZER_GENERAL(fname##_Register, \ ("EndGeneralStartupOptionRegistration"), \ ("EndStartupOptionRegistration")) @@ -90,9 +90,8 @@ * } */ #define MONGO_STARTUP_OPTIONS_PARSE(fname) \ - MONGO_INITIALIZER_GENERAL(fname##_Parse, \ - ("BeginStartupOptionParsing"), \ - ("EndStartupOptionParsing")) + MONGO_INITIALIZER_GENERAL( \ + fname##_Parse, ("BeginStartupOptionParsing"), ("EndStartupOptionParsing")) /** * Macro to define an initializer function named "<fname>_Validate" to validate the command line and @@ -107,9 +106,8 @@ * } */ #define MONGO_STARTUP_OPTIONS_VALIDATE(fname) \ - MONGO_INITIALIZER_GENERAL(fname##_Validate, \ - ("BeginStartupOptionValidation"), \ - ("EndStartupOptionValidation")) + MONGO_INITIALIZER_GENERAL( \ + fname##_Validate, ("BeginStartupOptionValidation"), ("EndStartupOptionValidation")) /** * Macro to define an initializer function named "<fname>_Store" to store the command line and @@ -125,6 +123,5 @@ * } */ #define MONGO_STARTUP_OPTIONS_STORE(fname) \ - MONGO_INITIALIZER_GENERAL(fname##_Store, \ - ("BeginStartupOptionStorage"), \ - ("EndStartupOptionStorage")) + MONGO_INITIALIZER_GENERAL( \ + fname##_Store, ("BeginStartupOptionStorage"), ("EndStartupOptionStorage")) diff --git a/src/mongo/util/options_parser/startup_options.cpp b/src/mongo/util/options_parser/startup_options.cpp index f38b6e8b3f3..587131aea41 100644 --- a/src/mongo/util/options_parser/startup_options.cpp +++ b/src/mongo/util/options_parser/startup_options.cpp @@ -34,8 +34,8 @@ namespace mongo { namespace optionenvironment { - OptionSection startupOptions("Options"); - Environment startupOptionsParsed; +OptionSection startupOptions("Options"); +Environment startupOptionsParsed; -} // namespace optionenvironment -} // namespace mongo +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/startup_options.h b/src/mongo/util/options_parser/startup_options.h index d6e7e9763a1..1ea7b7b8232 100644 --- a/src/mongo/util/options_parser/startup_options.h +++ b/src/mongo/util/options_parser/startup_options.h @@ -32,35 +32,35 @@ namespace mongo { namespace optionenvironment { - /* - * This structure stores information about all the command line options. The parser will use - * this description when it parses the command line, the INI config file, and the JSON config - * file. See the OptionSection and OptionDescription classes for more details. - * - * Example: - * MONGO_MODULE_STARTUP_OPTIONS_REGISTER(MongodOptions)(InitializerContext* context) { - * return addMongodOptions(&moe::startupOptions); - * startupOptions.addOptionChaining("option", "option", moe::String, "description"); - * return Status::OK(); - * } - */ - extern OptionSection startupOptions; +/* + * This structure stores information about all the command line options. The parser will use + * this description when it parses the command line, the INI config file, and the JSON config + * file. See the OptionSection and OptionDescription classes for more details. + * + * Example: + * MONGO_MODULE_STARTUP_OPTIONS_REGISTER(MongodOptions)(InitializerContext* context) { + * return addMongodOptions(&moe::startupOptions); + * startupOptions.addOptionChaining("option", "option", moe::String, "description"); + * return Status::OK(); + * } + */ +extern OptionSection startupOptions; - /* - * This structure stores the parsed command line options. After the "defult" group of the - * MONGO_INITIALIZERS, this structure should be fully validated from an option perspective. See - * the Environment, Constraint, and Value classes for more details. - * - * Example: - * if (startupOptionsParsed.count("option")) { - * std::string value; - * ret = startupOptionsParsed.get("option", &value); - * if (!ret.isOK()) { - * return ret; - * } - * } - */ - extern Environment startupOptionsParsed; +/* + * This structure stores the parsed command line options. After the "defult" group of the + * MONGO_INITIALIZERS, this structure should be fully validated from an option perspective. See + * the Environment, Constraint, and Value classes for more details. + * + * Example: + * if (startupOptionsParsed.count("option")) { + * std::string value; + * ret = startupOptionsParsed.get("option", &value); + * if (!ret.isOK()) { + * return ret; + * } + * } + */ +extern Environment startupOptionsParsed; -} // namespace optionenvironment -} // namespace mongo +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/value.cpp b/src/mongo/util/options_parser/value.cpp index f41092ab384..5c8d2d14fea 100644 --- a/src/mongo/util/options_parser/value.cpp +++ b/src/mongo/util/options_parser/value.cpp @@ -32,209 +32,251 @@ namespace mongo { namespace optionenvironment { - // Value implementation +// Value implementation - // Value access functions +// Value access functions - Status Value::get(StringVector_t* val) const { - if (_type != StringVector) { - StringBuilder sb; - sb << "Attempting to get Value as type: StringVector, but Value is of type: " - << typeToString(); - return Status(ErrorCodes::TypeMismatch, sb.str()); - } - *val = _stringVectorVal; - return Status::OK(); - } - Status Value::get(StringMap_t* val) const { - if (_type != StringMap) { - StringBuilder sb; - sb << "Attempting to get Value as type: StringMap, but Value is of type: " - << typeToString(); - return Status(ErrorCodes::TypeMismatch, sb.str()); - } - *val = _stringMapVal; - return Status::OK(); +Status Value::get(StringVector_t* val) const { + if (_type != StringVector) { + StringBuilder sb; + sb << "Attempting to get Value as type: StringVector, but Value is of type: " + << typeToString(); + return Status(ErrorCodes::TypeMismatch, sb.str()); } - Status Value::get(bool* val) const { - if (_type != Bool) { - StringBuilder sb; - sb << "Attempting to get Value as type: Bool, but Value is of type: " - << typeToString(); - return Status(ErrorCodes::TypeMismatch, sb.str()); - } - *val = _boolVal; - return Status::OK(); + *val = _stringVectorVal; + return Status::OK(); +} +Status Value::get(StringMap_t* val) const { + if (_type != StringMap) { + StringBuilder sb; + sb << "Attempting to get Value as type: StringMap, but Value is of type: " + << typeToString(); + return Status(ErrorCodes::TypeMismatch, sb.str()); } - Status Value::get(double* val) const { - if (_type != Double) { - StringBuilder sb; - sb << "Attempting to get Value as type: Double, but Value is of type: " - << typeToString(); - return Status(ErrorCodes::TypeMismatch, sb.str()); - } - *val = _doubleVal; - return Status::OK(); + *val = _stringMapVal; + return Status::OK(); +} +Status Value::get(bool* val) const { + if (_type != Bool) { + StringBuilder sb; + sb << "Attempting to get Value as type: Bool, but Value is of type: " << typeToString(); + return Status(ErrorCodes::TypeMismatch, sb.str()); } - Status Value::get(int* val) const { - if (_type != Int) { - StringBuilder sb; - sb << "Attempting to get Value as type: Int, but Value is of type: " - << typeToString(); - return Status(ErrorCodes::TypeMismatch, sb.str()); - } - *val = _intVal; - return Status::OK(); + *val = _boolVal; + return Status::OK(); +} +Status Value::get(double* val) const { + if (_type != Double) { + StringBuilder sb; + sb << "Attempting to get Value as type: Double, but Value is of type: " << typeToString(); + return Status(ErrorCodes::TypeMismatch, sb.str()); } - Status Value::get(long* val) const { - if (_type == Long) { - *val = _longVal; - return Status::OK(); - } - else if (_type == Int) { - *val = _intVal; - return Status::OK(); - } + *val = _doubleVal; + return Status::OK(); +} +Status Value::get(int* val) const { + if (_type != Int) { StringBuilder sb; - sb << "Value of type: " << typeToString() - << " is not convertible to type: Long"; + sb << "Attempting to get Value as type: Int, but Value is of type: " << typeToString(); return Status(ErrorCodes::TypeMismatch, sb.str()); } - Status Value::get(std::string* val) const { - if (_type != String) { - StringBuilder sb; - sb << "Attempting to get Value as type: string, but Value is of type: " - << typeToString(); - return Status(ErrorCodes::TypeMismatch, sb.str()); - } - *val = _stringVal; + *val = _intVal; + return Status::OK(); +} +Status Value::get(long* val) const { + if (_type == Long) { + *val = _longVal; + return Status::OK(); + } else if (_type == Int) { + *val = _intVal; return Status::OK(); } - Status Value::get(unsigned long long* val) const { - if (_type == UnsignedLongLong) { - *val = _unsignedLongLongVal; - return Status::OK(); - } - else if (_type == Unsigned) { - *val = _unsignedVal; - return Status::OK(); - } + StringBuilder sb; + sb << "Value of type: " << typeToString() << " is not convertible to type: Long"; + return Status(ErrorCodes::TypeMismatch, sb.str()); +} +Status Value::get(std::string* val) const { + if (_type != String) { StringBuilder sb; - sb << "Value of type: " << typeToString() - << " is not convertible to type: UnsignedLongLong"; + sb << "Attempting to get Value as type: string, but Value is of type: " << typeToString(); return Status(ErrorCodes::TypeMismatch, sb.str()); } - Status Value::get(unsigned* val) const { - if (_type != Unsigned) { - StringBuilder sb; - sb << "Attempting to get Value as type: Unsigned, but Value is of type: " - << typeToString(); - return Status(ErrorCodes::TypeMismatch, sb.str()); - } + *val = _stringVal; + return Status::OK(); +} +Status Value::get(unsigned long long* val) const { + if (_type == UnsignedLongLong) { + *val = _unsignedLongLongVal; + return Status::OK(); + } else if (_type == Unsigned) { *val = _unsignedVal; return Status::OK(); } + StringBuilder sb; + sb << "Value of type: " << typeToString() << " is not convertible to type: UnsignedLongLong"; + return Status(ErrorCodes::TypeMismatch, sb.str()); +} +Status Value::get(unsigned* val) const { + if (_type != Unsigned) { + StringBuilder sb; + sb << "Attempting to get Value as type: Unsigned, but Value is of type: " << typeToString(); + return Status(ErrorCodes::TypeMismatch, sb.str()); + } + *val = _unsignedVal; + return Status::OK(); +} - // Value utility functions +// Value utility functions - std::string Value::typeToString() const { - switch (_type) { - case StringVector: return "StringVector"; - case StringMap: return "StringMap"; - case Bool: return "Bool"; - case Double: return "Double"; - case Int: return "Int"; - case Long: return "Long"; - case String: return "String"; - case UnsignedLongLong: return "UnsignedLongLong"; - case Unsigned: return "Unsigned"; - case None: return "None"; - default: return "Unknown"; - } +std::string Value::typeToString() const { + switch (_type) { + case StringVector: + return "StringVector"; + case StringMap: + return "StringMap"; + case Bool: + return "Bool"; + case Double: + return "Double"; + case Int: + return "Int"; + case Long: + return "Long"; + case String: + return "String"; + case UnsignedLongLong: + return "UnsignedLongLong"; + case Unsigned: + return "Unsigned"; + case None: + return "None"; + default: + return "Unknown"; } - bool Value::isEmpty() const { - return _type == None; +} +bool Value::isEmpty() const { + return _type == None; +} +bool Value::equal(const Value& otherVal) const { + if (_type != otherVal._type) { + return false; } - bool Value::equal(const Value& otherVal) const { - if (_type != otherVal._type) { - return false; - } - switch (_type) { - case StringVector: return _stringVectorVal == otherVal._stringVectorVal; - case StringMap: return _stringMapVal == otherVal._stringMapVal; - case Bool: return _boolVal == otherVal._boolVal; - case Double: return _doubleVal == otherVal._doubleVal; - case Int: return _intVal == otherVal._intVal; - case Long: return _longVal == otherVal._longVal; - case String: return _stringVal == otherVal._stringVal; - case UnsignedLongLong: return _unsignedLongLongVal == otherVal._unsignedLongLongVal; - case Unsigned: return _unsignedVal == otherVal._unsignedVal; - case None: return true; - default: return false; /* Undefined */ - } + switch (_type) { + case StringVector: + return _stringVectorVal == otherVal._stringVectorVal; + case StringMap: + return _stringMapVal == otherVal._stringMapVal; + case Bool: + return _boolVal == otherVal._boolVal; + case Double: + return _doubleVal == otherVal._doubleVal; + case Int: + return _intVal == otherVal._intVal; + case Long: + return _longVal == otherVal._longVal; + case String: + return _stringVal == otherVal._stringVal; + case UnsignedLongLong: + return _unsignedLongLongVal == otherVal._unsignedLongLongVal; + case Unsigned: + return _unsignedVal == otherVal._unsignedVal; + case None: + return true; + default: + return false; /* Undefined */ } +} - // Dump the value as a string. This function is used only for debugging purposes. - std::string Value::toString() const { - StringBuilder sb; - switch (_type) { - case StringVector: - if (!_stringVectorVal.empty()) - { - // Convert all but the last element to avoid a trailing "," - for (StringVector_t::const_iterator iterator = _stringVectorVal.begin(); - iterator != _stringVectorVal.end() - 1; iterator++) { - sb << *iterator << ","; - } - - // Now add the last element with no delimiter - sb << _stringVectorVal.back(); +// Dump the value as a string. This function is used only for debugging purposes. +std::string Value::toString() const { + StringBuilder sb; + switch (_type) { + case StringVector: + if (!_stringVectorVal.empty()) { + // Convert all but the last element to avoid a trailing "," + for (StringVector_t::const_iterator iterator = _stringVectorVal.begin(); + iterator != _stringVectorVal.end() - 1; + iterator++) { + sb << *iterator << ","; } - break; - case StringMap: - if (!_stringMapVal.empty()) - { - // Convert all but the last element to avoid a trailing "," - if (_stringMapVal.begin() != _stringMapVal.end()) { - StringMap_t::const_iterator iterator; - StringMap_t::const_iterator it_last; - for (iterator = _stringMapVal.begin(), it_last = --_stringMapVal.end(); - iterator != it_last; ++iterator) { - sb << iterator->first << ":" << iterator->second << ","; - } - } - // Now add the last element with no delimiter - sb << _stringMapVal.end()->first << ":" << _stringMapVal.end()->second; + // Now add the last element with no delimiter + sb << _stringVectorVal.back(); + } + break; + case StringMap: + if (!_stringMapVal.empty()) { + // Convert all but the last element to avoid a trailing "," + if (_stringMapVal.begin() != _stringMapVal.end()) { + StringMap_t::const_iterator iterator; + StringMap_t::const_iterator it_last; + for (iterator = _stringMapVal.begin(), it_last = --_stringMapVal.end(); + iterator != it_last; + ++iterator) { + sb << iterator->first << ":" << iterator->second << ","; + } } - break; - case Bool: sb << _boolVal; break; - case Double: sb << _doubleVal; break; - case Int: sb << _intVal; break; - case Long: sb << _longVal; break; - case String: sb << _stringVal; break; - case UnsignedLongLong: sb << _unsignedLongLongVal; break; - case Unsigned: sb << _unsignedVal; break; - case None: sb << "(not set)"; break; - default: sb << "(undefined)"; break; - } - return sb.str(); + + // Now add the last element with no delimiter + sb << _stringMapVal.end()->first << ":" << _stringMapVal.end()->second; + } + break; + case Bool: + sb << _boolVal; + break; + case Double: + sb << _doubleVal; + break; + case Int: + sb << _intVal; + break; + case Long: + sb << _longVal; + break; + case String: + sb << _stringVal; + break; + case UnsignedLongLong: + sb << _unsignedLongLongVal; + break; + case Unsigned: + sb << _unsignedVal; + break; + case None: + sb << "(not set)"; + break; + default: + sb << "(undefined)"; + break; } - const std::type_info& Value::type() const { - switch (_type) { - case StringVector: return typeid(StringVector_t); - case StringMap: return typeid(StringMap_t); - case Bool: return typeid(bool); - case Double: return typeid(double); - case Int: return typeid(int); - case Long: return typeid(long); - case String: return typeid(std::string); - case UnsignedLongLong: return typeid(unsigned long long); - case Unsigned: return typeid(unsigned); - case None: return typeid(void); - default: return typeid(void); - } + return sb.str(); +} +const std::type_info& Value::type() const { + switch (_type) { + case StringVector: + return typeid(StringVector_t); + case StringMap: + return typeid(StringMap_t); + case Bool: + return typeid(bool); + case Double: + return typeid(double); + case Int: + return typeid(int); + case Long: + return typeid(long); + case String: + return typeid(std::string); + case UnsignedLongLong: + return typeid(unsigned long long); + case Unsigned: + return typeid(unsigned); + case None: + return typeid(void); + default: + return typeid(void); } +} -} // namespace optionenvironment -} // namespace mongo +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/value.h b/src/mongo/util/options_parser/value.h index 9c9bb1468d7..2f7a089ea80 100644 --- a/src/mongo/util/options_parser/value.h +++ b/src/mongo/util/options_parser/value.h @@ -36,147 +36,145 @@ namespace mongo { namespace optionenvironment { - class Constraint; - class KeyConstraint; +class Constraint; +class KeyConstraint; + +/** + * Helper typedefs for the more complex C++ types supported by this Value class + */ +typedef std::map<std::string, std::string> StringMap_t; +typedef std::vector<std::string> StringVector_t; + +typedef std::string Key; + +/** A simple container interface for storing various C++ values. + * + * Usage: + * + * Value intVal(2); + * Value stringVal("string"); + * + * int intContents = 1; + * Status ret = stringVal.get(&intContents); + * // ret != Status::OK() + * // intContents is still 1 + * + * ret = intVal.get(&intContents); + * // ret == Status::OK() + * // intContents is now 2 + */ +class Value { +public: + // Constructors + + explicit Value() : _type(None) {} + explicit Value(StringVector_t val) : _stringVectorVal(val), _type(StringVector) {} + explicit Value(StringMap_t val) : _stringMapVal(val), _type(StringMap) {} + explicit Value(bool val) : _boolVal(val), _type(Bool) {} + explicit Value(double val) : _doubleVal(val), _type(Double) {} + explicit Value(int val) : _intVal(val), _type(Int) {} + explicit Value(long val) : _longVal(val), _type(Long) {} + explicit Value(std::string val) : _stringVal(val), _type(String) {} + explicit Value(unsigned long long val) : _unsignedLongLongVal(val), _type(UnsignedLongLong) {} + explicit Value(unsigned val) : _unsignedVal(val), _type(Unsigned) {} + + // Access interface + + Status get(StringVector_t* val) const; + Status get(StringMap_t* val) const; + Status get(bool* val) const; + Status get(double* val) const; + Status get(int* val) const; + Status get(long* val) const; + Status get(std::string* val) const; + Status get(unsigned long long* val) const; + Status get(unsigned* val) const; + + // Utility functions /** - * Helper typedefs for the more complex C++ types supported by this Value class + * Return the value's type as a string */ - typedef std::map<std::string, std::string> StringMap_t; - typedef std::vector<std::string> StringVector_t; + std::string typeToString() const; - typedef std::string Key; + /** + * Return true if the value was created with the no argument constructor + */ + bool isEmpty() const; - /** A simple container interface for storing various C++ values. - * - * Usage: - * - * Value intVal(2); - * Value stringVal("string"); - * - * int intContents = 1; - * Status ret = stringVal.get(&intContents); - * // ret != Status::OK() - * // intContents is still 1 + /** + * Return true if the other Value equals this value, both in type and in contents * - * ret = intVal.get(&intContents); - * // ret == Status::OK() - * // intContents is now 2 + * Two empty values are equal */ - class Value { - public: - - // Constructors - - explicit Value() : _type(None) { } - explicit Value(StringVector_t val) : _stringVectorVal(val), _type(StringVector) {} - explicit Value(StringMap_t val) : _stringMapVal(val), _type(StringMap) {} - explicit Value(bool val) : _boolVal(val), _type(Bool) { } - explicit Value(double val) : _doubleVal(val), _type(Double) { } - explicit Value(int val) : _intVal(val), _type(Int) { } - explicit Value(long val) : _longVal(val), _type(Long) { } - explicit Value(std::string val) : _stringVal(val), _type(String) { } - explicit Value(unsigned long long val) : _unsignedLongLongVal(val), - _type(UnsignedLongLong) { } - explicit Value(unsigned val) : _unsignedVal(val), _type(Unsigned) { } - - // Access interface - - Status get(StringVector_t* val) const; - Status get(StringMap_t* val) const; - Status get(bool* val) const; - Status get(double* val) const; - Status get(int* val) const; - Status get(long* val) const; - Status get(std::string* val) const; - Status get(unsigned long long* val) const; - Status get(unsigned* val) const; - - // Utility functions - - /** - * Return the value's type as a string - */ - std::string typeToString() const; - - /** - * Return true if the value was created with the no argument constructor - */ - bool isEmpty() const; - - /** - * Return true if the other Value equals this value, both in type and in contents - * - * Two empty values are equal - */ - bool equal(const Value&) const; - - /** - * Return the std::string representation of this Value. This function is used only for - * debugging purposes and does not output data in an easily parseable format. - */ - std::string toString() const; - - /** - * The functions below are the legacy interface to be consistent with boost::any during the - * transition period - */ - - /** - * Returns the contents of this Value as type T. Throws MsgAssertionException if the type - * does not match - */ - template <typename T> - T as() const; - - /** - * Return the type_info for this value - */ - const std::type_info& type() const; - - private: - StringVector_t _stringVectorVal; - StringMap_t _stringMapVal; - std::string _stringVal; - union { - bool _boolVal; - double _doubleVal; - int _intVal; - long _longVal; - unsigned long long _unsignedLongLongVal; - unsigned _unsignedVal; - }; - - // Types currently supported by Value - enum Type { - StringVector, // std::vector<std::string> - StringMap, // std::map<std::string, std::string> - Bool, // bool - Double, // double - Int, // int - Long, // long - String, // std::string - UnsignedLongLong, // unsigned long long - Unsigned, // unsigned - None, // (not set) - }; - - Type _type; - }; + bool equal(const Value&) const; + /** + * Return the std::string representation of this Value. This function is used only for + * debugging purposes and does not output data in an easily parseable format. + */ + std::string toString() const; + + /** + * The functions below are the legacy interface to be consistent with boost::any during the + * transition period + */ + + /** + * Returns the contents of this Value as type T. Throws MsgAssertionException if the type + * does not match + */ template <typename T> - T Value::as() const { - T valueType; + T as() const; - Status ret = get(&valueType); - if (!ret.isOK()) { - StringBuilder message; - message << "failed to extract typed value from Value container: " << ret.toString(); - throw MsgAssertionException(17114, message.str()); - } + /** + * Return the type_info for this value + */ + const std::type_info& type() const; + +private: + StringVector_t _stringVectorVal; + StringMap_t _stringMapVal; + std::string _stringVal; + union { + bool _boolVal; + double _doubleVal; + int _intVal; + long _longVal; + unsigned long long _unsignedLongLongVal; + unsigned _unsignedVal; + }; + + // Types currently supported by Value + enum Type { + StringVector, // std::vector<std::string> + StringMap, // std::map<std::string, std::string> + Bool, // bool + Double, // double + Int, // int + Long, // long + String, // std::string + UnsignedLongLong, // unsigned long long + Unsigned, // unsigned + None, // (not set) + }; + + Type _type; +}; - return valueType; +template <typename T> +T Value::as() const { + T valueType; + + Status ret = get(&valueType); + if (!ret.isOK()) { + StringBuilder message; + message << "failed to extract typed value from Value container: " << ret.toString(); + throw MsgAssertionException(17114, message.str()); } -} // namespace optionenvironment -} // namespace mongo + return valueType; +} + +} // namespace optionenvironment +} // namespace mongo |