Repository: mesos Updated Branches: refs/heads/master 53acb048c -> 559623338
Added validation to flags. Also refactored existing 'lambda::bind' arguments to use C++11 lambdas, enabling us to get rid of our "loader" and "stringifier" functors. Review: https://reviews.apache.org/r/34943 Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/55962333 Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/55962333 Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/55962333 Branch: refs/heads/master Commit: 5596233382844da05b82a7769c726a8cdd1bfa17 Parents: 53acb04 Author: Benjamin Hindman <[email protected]> Authored: Tue Jun 2 06:21:56 2015 -0700 Committer: Benjamin Hindman <[email protected]> Committed: Thu Jun 18 09:24:43 2015 -0700 ---------------------------------------------------------------------- .../3rdparty/stout/include/Makefile.am | 2 - .../3rdparty/stout/include/stout/flags/flag.hpp | 4 +- .../stout/include/stout/flags/flags.hpp | 333 ++++++++++++++----- .../stout/include/stout/flags/loader.hpp | 122 ------- .../stout/include/stout/flags/stringifier.hpp | 78 ----- .../3rdparty/stout/tests/flags_tests.cpp | 38 +++ 6 files changed, 292 insertions(+), 285 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/55962333/3rdparty/libprocess/3rdparty/stout/include/Makefile.am ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/3rdparty/stout/include/Makefile.am b/3rdparty/libprocess/3rdparty/stout/include/Makefile.am index 6ac2f04..cb53180 100644 --- a/3rdparty/libprocess/3rdparty/stout/include/Makefile.am +++ b/3rdparty/libprocess/3rdparty/stout/include/Makefile.am @@ -15,9 +15,7 @@ nobase_include_HEADERS = \ stout/flags/fetch.hpp \ stout/flags/flag.hpp \ stout/flags/flags.hpp \ - stout/flags/loader.hpp \ stout/flags/parse.hpp \ - stout/flags/stringifier.hpp \ stout/foreach.hpp \ stout/format.hpp \ stout/fs.hpp \ http://git-wip-us.apache.org/repos/asf/mesos/blob/55962333/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flag.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flag.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flag.hpp index 87606d8..a289f83 100644 --- a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flag.hpp +++ b/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flag.hpp @@ -16,6 +16,7 @@ #include <string> +#include <stout/error.hpp> #include <stout/lambda.hpp> #include <stout/nothing.hpp> #include <stout/try.hpp> @@ -30,8 +31,9 @@ struct Flag std::string name; std::string help; bool boolean; - lambda::function<Try<Nothing>(FlagsBase*, const std::string&)> loader; + lambda::function<Try<Nothing>(FlagsBase*, const std::string&)> load; lambda::function<Option<std::string>(const FlagsBase&)> stringify; + lambda::function<Option<Error>(const FlagsBase&)> validate; }; } // namespace flags { http://git-wip-us.apache.org/repos/asf/mesos/blob/55962333/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flags.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flags.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flags.hpp index ee855da..7584cb8 100644 --- a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flags.hpp +++ b/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flags.hpp @@ -34,8 +34,6 @@ #include <stout/flags/fetch.hpp> #include <stout/flags/flag.hpp> -#include <stout/flags/loader.hpp> -#include <stout/flags/stringifier.hpp> namespace flags { @@ -78,7 +76,7 @@ public: bool duplicates = false); virtual Try<Nothing> load( - const std::map<std::string, Option<std::string> >& values, + const std::map<std::string, Option<std::string>>& values, bool unknowns = false); virtual Try<Nothing> load( @@ -137,28 +135,74 @@ public: iterator begin() { return flags_.begin(); } iterator end() { return flags_.end(); } + template <typename T1, typename T2, typename F> + void add( + T1* t1, + const std::string& name, + const std::string& help, + const T2& t2, + F validate); + template <typename T1, typename T2> - void add(T1* t1, - const std::string& name, - const std::string& help, - const T2& t2); + void add( + T1* t1, + const std::string& name, + const std::string& help, + const T2& t2) + { + add(t1, name, help, t2, [](const T1&) { return None(); }); + } + + template <typename T, typename F> + void add( + Option<T>* option, + const std::string& name, + const std::string& help, + F validate); template <typename T> - void add(Option<T>* option, - const std::string& name, - const std::string& help); + void add( + Option<T>* option, + const std::string& name, + const std::string& help) + { + add(option, name, help, [](const Option<T>&) { return None(); }); + } protected: + template <typename Flags, typename T1, typename T2, typename F> + void add( + T1 Flags::*t1, + const std::string& name, + const std::string& help, + const T2& t2, + F validate); + template <typename Flags, typename T1, typename T2> - void add(T1 Flags::*t1, - const std::string& name, - const std::string& help, - const T2& t2); + void add( + T1 Flags::*t1, + const std::string& name, + const std::string& help, + const T2& t2) + { + add(t1, name, help, t2, [](const T1&) { return None(); }); + } + + template <typename Flags, typename T, typename F> + void add( + Option<T> Flags::*option, + const std::string& name, + const std::string& help, + F validate); template <typename Flags, typename T> - void add(Option<T> Flags::*option, - const std::string& name, - const std::string& help); + void add( + Option<T> Flags::*option, + const std::string& name, + const std::string& help) + { + add(option, name, help, [](const Option<T>&) { return None(); }); + } void add(const Flag& flag); @@ -183,7 +227,7 @@ protected: private: // Extract environment variable "flags" with the specified prefix. - std::map<std::string, Option<std::string> > extract( + std::map<std::string, Option<std::string>> extract( const std::string& prefix); std::map<std::string, Flag> flags_; @@ -214,27 +258,51 @@ class Flags : public virtual Flags1, public virtual Flags5 {}; -template <typename T1, typename T2> +template <typename T1, typename T2, typename F> void FlagsBase::add( T1* t1, const std::string& name, const std::string& help, - const T2& t2) + const T2& t2, + F validate) { + // Don't bother adding anything if the pointer is NULL. + if (t1 == NULL) { + return; + } + *t1 = t2; // Set the default. Flag flag; flag.name = name; flag.help = help; flag.boolean = typeid(T1) == typeid(bool); - flag.loader = lambda::bind( - &Loader<T1>::load, - t1, - lambda::function<Try<T1>(const std::string&)>( - lambda::bind(&fetch<T1>, lambda::_1)), - name, - lambda::_2); // Use _2 because ignore FlagsBase*. - flag.stringify = lambda::bind(&Stringifier<T1>, t1); + + // NOTE: We need to take FlagsBase* (or const FlagsBase&) as the + // first argument to match the function signature of the 'load', + // 'stringify', and 'validate' lambdas used in other overloads of + // FlagsBase::add. Since we don't need to use the pointer here we + // don't name it as a parameter. + + flag.load = [t1](FlagsBase*, const std::string& value) -> Try<Nothing> { + // NOTE: 'fetch' "retrieves" the value if necessary and then + // invokes 'parse'. See 'fetch' for more details. + Try<T1> t = fetch<T1>(value); + if (t.isSome()) { + *t1 = t.get(); + } else { + return Error("Failed to load value '" + value + "': " + t.error()); + } + return Nothing(); + }; + + flag.stringify = [t1](const FlagsBase&) -> Option<std::string> { + return stringify(*t1); + }; + + flag.validate = [t1, validate](const FlagsBase&) -> Option<Error> { + return validate(*t1); + }; // Update the help string to include the default value. flag.help += help.size() > 0 && help.find_last_of("\n\r") != help.size() - 1 @@ -243,40 +311,70 @@ void FlagsBase::add( flag.help += stringify(t2); flag.help += ")"; - FlagsBase::add(flag); + add(flag); } -template <typename T> +template <typename T, typename F> void FlagsBase::add( Option<T>* option, const std::string& name, - const std::string& help) + const std::string& help, + F validate) { + // Don't bother adding anything if the pointer is NULL. + if (option == NULL) { + return; + } + Flag flag; flag.name = name; flag.help = help; flag.boolean = typeid(T) == typeid(bool); - flag.loader = lambda::bind( - &OptionLoader<T>::load, - option, - lambda::function<Try<T>(const std::string&)>( - lambda::bind(&fetch<T>, lambda::_1)), - name, - lambda::_2); // Use _2 because ignore FlagsBase*. - flag.stringify = lambda::bind(&OptionStringifier<T>, option); - - FlagsBase::add(flag); + + // NOTE: See comment above in T* overload of FlagsBase::add for why + // we need to take the FlagsBase* parameter. + + flag.load = [option](FlagsBase*, const std::string& value) -> Try<Nothing> { + // NOTE: 'fetch' "retrieves" the value if necessary and then + // invokes 'parse'. See 'fetch' for more details. + Try<T> t = fetch<T>(value); + if (t.isSome()) { + *option = Some(t.get()); + } else { + return Error("Failed to load value '" + value + "': " + t.error()); + } + return Nothing(); + }; + + flag.stringify = [option](const FlagsBase&) -> Option<std::string> { + if (option->isSome()) { + return stringify(option->get()); + } + return None(); + }; + + flag.validate = [option, validate](const FlagsBase&) -> Option<Error> { + return validate(*option); + }; + + add(flag); } -template <typename Flags, typename T1, typename T2> +template <typename Flags, typename T1, typename T2, typename F> void FlagsBase::add( T1 Flags::*t1, const std::string& name, const std::string& help, - const T2& t2) + const T2& t2, + F validate) { + // Don't bother adding anything if the pointer is NULL. + if (t1 == NULL) { + return; + } + Flags* flags = dynamic_cast<Flags*>(this); if (flags == NULL) { ABORT("Attempted to add flag '" + name + "' with incompatible type"); @@ -288,18 +386,45 @@ void FlagsBase::add( flag.name = name; flag.help = help; flag.boolean = typeid(T1) == typeid(bool); - flag.loader = lambda::bind( - &MemberLoader<Flags, T1>::load, - lambda::_1, - t1, - lambda::function<Try<T1>(const std::string&)>( - lambda::bind(&fetch<T1>, lambda::_1)), - name, - lambda::_2); - flag.stringify = lambda::bind( - &MemberStringifier<Flags, T1>, - lambda::_1, - t1); + + // NOTE: We need to take FlagsBase* (or const FlagsBase&) as the + // first argument to 'load', 'stringify', and 'validate' so that we + // use the correct instance of FlagsBase. In other words, we can't + // capture 'this' here because it's possible that the FlagsBase + // object that we're working with when we invoke FlagsBase::add is + // not the same instance as 'this' when the these lambdas get + // invoked. + + flag.load = [t1](FlagsBase* base, const std::string& value) -> Try<Nothing> { + Flags* flags = dynamic_cast<Flags*>(base); + if (base != NULL) { + // NOTE: 'fetch' "retrieves" the value if necessary and then + // invokes 'parse'. See 'fetch' for more details. + Try<T1> t = fetch<T1>(value); + if (t.isSome()) { + flags->*t1 = t.get(); + } else { + return Error("Failed to load value '" + value + "': " + t.error()); + } + } + return Nothing(); + }; + + flag.stringify = [t1](const FlagsBase& base) -> Option<std::string> { + const Flags* flags = dynamic_cast<const Flags*>(&base); + if (flags != NULL) { + return stringify(flags->*t1); + } + return None(); + }; + + flag.validate = [t1, validate](const FlagsBase& base) -> Option<Error> { + const Flags* flags = dynamic_cast<const Flags*>(&base); + if (flags != NULL) { + return validate(flags->*t1); + } + return None(); + }; // Update the help string to include the default value. flag.help += help.size() > 0 && help.find_last_of("\n\r") != help.size() - 1 @@ -312,12 +437,18 @@ void FlagsBase::add( } -template <typename Flags, typename T> +template <typename Flags, typename T, typename F> void FlagsBase::add( Option<T> Flags::*option, const std::string& name, - const std::string& help) + const std::string& help, + F validate) { + // Don't bother adding anything if the pointer is NULL. + if (option == NULL) { + return; + } + Flags* flags = dynamic_cast<Flags*>(this); if (flags == NULL) { ABORT("Attempted to add flag '" + name + "' with incompatible type"); @@ -327,18 +458,44 @@ void FlagsBase::add( flag.name = name; flag.help = help; flag.boolean = typeid(T) == typeid(bool); - flag.loader = lambda::bind( - &OptionMemberLoader<Flags, T>::load, - lambda::_1, - option, - lambda::function<Try<T>(const std::string&)>( - lambda::bind(&fetch<T>, lambda::_1)), - name, - lambda::_2); - flag.stringify = lambda::bind( - &OptionMemberStringifier<Flags, T>, - lambda::_1, - option); + + // NOTE: See comment above in Flags::T* overload of FLagsBase::add + // for why we need to pass FlagsBase* (or const FlagsBase&) as a + // parameter. + + flag.load = + [option](FlagsBase* base, const std::string& value) -> Try<Nothing> { + Flags* flags = dynamic_cast<Flags*>(base); + if (flags != NULL) { + // NOTE: 'fetch' "retrieves" the value if necessary and then + // invokes 'parse'. See 'fetch' for more details. + Try<T> t = fetch<T>(value); + if (t.isSome()) { + flags->*option = Some(t.get()); + } else { + return Error("Failed to load value '" + value + "': " + t.error()); + } + } + return Nothing(); + }; + + flag.stringify = [option](const FlagsBase& base) -> Option<std::string> { + const Flags* flags = dynamic_cast<const Flags*>(&base); + if (flags != NULL) { + if ((flags->*option).isSome()) { + return stringify((flags->*option).get()); + } + } + return None(); + }; + + flag.validate = [option, validate](const FlagsBase& base) -> Option<Error> { + const Flags* flags = dynamic_cast<const Flags*>(&base); + if (flags != NULL) { + return validate(flags->*option); + } + return None(); + }; add(flag); } @@ -358,10 +515,10 @@ inline void FlagsBase::add(const Flag& flag) // Extract environment variable "flags" with the specified prefix. -inline std::map<std::string, Option<std::string> > FlagsBase::extract( +inline std::map<std::string, Option<std::string>> FlagsBase::extract( const std::string& prefix) { - std::map<std::string, Option<std::string> > values; + std::map<std::string, Option<std::string>> values; foreachpair (const std::string& key, const std::string& value, @@ -395,7 +552,7 @@ inline Try<Nothing> FlagsBase::load( bool unknowns, bool duplicates) { - std::map<std::string, Option<std::string> > values; + std::map<std::string, Option<std::string>> values; // Grab the program name from argv[0]. programName_ = argc > 0 ? os::basename(argv[0]).get() : ""; @@ -456,7 +613,7 @@ inline Try<Nothing> FlagsBase::load( bool unknowns, bool duplicates) { - std::map<std::string, Option<std::string> > values; + std::map<std::string, Option<std::string>> values; if (prefix.isSome()) { values = extract(prefix.get()); @@ -535,10 +692,10 @@ inline Try<Nothing> FlagsBase::load( inline Try<Nothing> FlagsBase::load( - const std::map<std::string, Option<std::string> >& values, + const std::map<std::string, Option<std::string>>& values, bool unknowns) { - std::map<std::string, Option<std::string> >::const_iterator iterator; + std::map<std::string, Option<std::string>>::const_iterator iterator; for (iterator = values.begin(); iterator != values.end(); ++iterator) { const std::string& name = iterator->first; @@ -547,17 +704,17 @@ inline Try<Nothing> FlagsBase::load( if (flags_.count(name) > 0) { if (value.isSome()) { // --name=value if (flags_[name].boolean && value.get() == "") { - flags_[name].loader(this, "true"); // Should never fail. + flags_[name].load(this, "true"); // Should never fail. } else { - Try<Nothing> loader = flags_[name].loader(this, value.get()); - if (loader.isError()) { + Try<Nothing> load = flags_[name].load(this, value.get()); + if (load.isError()) { return Error( - "Failed to load flag '" + name + "': " + loader.error()); + "Failed to load flag '" + name + "': " + load.error()); } } } else { // --name if (flags_[name].boolean) { - flags_[name].loader(this, "true"); // Should never fail. + flags_[name].load(this, "true"); // Should never fail. } else { return Error( "Failed to load non-boolean flag '" + name + "': Missing value"); @@ -567,7 +724,7 @@ inline Try<Nothing> FlagsBase::load( if (flags_.count(name.substr(3)) > 0) { // --no-name if (flags_[name.substr(3)].boolean) { if (value.isNone() || value.get() == "") { - flags_[name.substr(3)].loader(this, "false"); // Should never fail. + flags_[name.substr(3)].load(this, "false"); // Should never fail. } else { return Error( "Failed to load boolean flag '" + name.substr(3) + @@ -588,6 +745,18 @@ inline Try<Nothing> FlagsBase::load( } } + // Validate the flags value. + // + // TODO(benh): Consider validating all flags at the same time in + // order to provide more feedback rather than requiring a user to + // fix one at a time. + foreachvalue (const Flag& flag, flags_) { + Option<Error> error = flag.validate(*this); + if (error.isSome()) { + return error.get(); + } + } + return Nothing(); } @@ -596,7 +765,7 @@ inline Try<Nothing> FlagsBase::load( const std::map<std::string, std::string>& _values, bool unknowns) { - std::map<std::string, Option<std::string> > values; + std::map<std::string, Option<std::string>> values; std::map<std::string, std::string>::const_iterator iterator; for (iterator = _values.begin(); iterator != _values.end(); ++iterator) { const std::string& name = iterator->first; http://git-wip-us.apache.org/repos/asf/mesos/blob/55962333/3rdparty/libprocess/3rdparty/stout/include/stout/flags/loader.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/loader.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/flags/loader.hpp deleted file mode 100644 index 51d3ab0..0000000 --- a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/loader.hpp +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __STOUT_FLAGS_LOADER_HPP__ -#define __STOUT_FLAGS_LOADER_HPP__ - -#include <string> - -#include <stout/error.hpp> -#include <stout/lambda.hpp> -#include <stout/nothing.hpp> -#include <stout/option.hpp> -#include <stout/some.hpp> -#include <stout/try.hpp> - -#include <stout/flags/parse.hpp> - -namespace flags { - -// Forward declaration. -class FlagsBase; - -template <typename T> -struct Loader -{ - static Try<Nothing> load( - T* flag, - const lambda::function<Try<T>(const std::string&)>& parse, - const std::string& name, - const std::string& value) - { - Try<T> t = parse(value); - if (t.isSome()) { - *flag = t.get(); - } else { - return Error("Failed to load value '" + value + "': " + t.error()); - } - return Nothing(); - } -}; - - -template <typename T> -struct OptionLoader -{ - static Try<Nothing> load( - Option<T>* flag, - const lambda::function<Try<T>(const std::string&)>& parse, - const std::string& name, - const std::string& value) - { - Try<T> t = parse(value); - if (t.isSome()) { - *flag = Some(t.get()); - } else { - return Error("Failed to load value '" + value + "': " + t.error()); - } - return Nothing(); - } -}; - - -template <typename F, typename T> -struct MemberLoader -{ - static Try<Nothing> load( - FlagsBase* base, - T F::*flag, - const lambda::function<Try<T>(const std::string&)>& parse, - const std::string& name, - const std::string& value) - { - F* f = dynamic_cast<F*>(base); - if (f != NULL) { - Try<T> t = parse(value); - if (t.isSome()) { - f->*flag = t.get(); - } else { - return Error("Failed to load value '" + value + "': " + t.error()); - } - } - return Nothing(); - } -}; - - -template <typename F, typename T> -struct OptionMemberLoader -{ - static Try<Nothing> load( - FlagsBase* base, - Option<T> F::*flag, - const lambda::function<Try<T>(const std::string&)>& parse, - const std::string& name, - const std::string& value) - { - F* f = dynamic_cast<F*>(base); - if (f != NULL) { - Try<T> t = parse(value); - if (t.isSome()) { - f->*flag = Some(t.get()); - } else { - return Error("Failed to load value '" + value + "': " + t.error()); - } - } - return Nothing(); - } -}; - -} // namespace flags { - -#endif // __STOUT_FLAGS_LOADER_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/55962333/3rdparty/libprocess/3rdparty/stout/include/stout/flags/stringifier.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/stringifier.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/flags/stringifier.hpp deleted file mode 100644 index fda5ae1..0000000 --- a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/stringifier.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __STOUT_FLAGS_STRINGIFIER_HPP__ -#define __STOUT_FLAGS_STRINGIFIER_HPP__ - -#include <string> - -#include <stout/error.hpp> -#include <stout/nothing.hpp> -#include <stout/option.hpp> -#include <stout/try.hpp> - -#include <stout/flags/parse.hpp> - -namespace flags { - -// Forward declaration. -class FlagsBase; - -template<typename T> -static Option<std::string> Stringifier(T* value) -{ - return stringify(*value); -} - - -template<typename T> -static Option<std::string> OptionStringifier(Option<T>* value) -{ - if (value->isSome()) { - return stringify(value->get()); - } - return None(); -} - - -template<typename F, typename T> -static Option<std::string> MemberStringifier( - const FlagsBase& base, - T F::*flag) -{ - const F* f = dynamic_cast<const F*>(&base); - if (f != NULL) { - return stringify(f->*flag); - } - return None(); -} - - -template<typename F, typename T> -static Option<std::string> OptionMemberStringifier( - const FlagsBase& base, - Option<T> F::*flag) -{ - const F* f = dynamic_cast<const F*>(&base); - if (f != NULL) { - const Option<T>& v = f->*flag; - if (v.isSome()) { - return stringify(v.get()); - } - } - return None(); -} - -} // namespace flags { - -#endif // __STOUT_FLAGS_STRINGIFIER_HPP__ http://git-wip-us.apache.org/repos/asf/mesos/blob/55962333/3rdparty/libprocess/3rdparty/stout/tests/flags_tests.cpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/3rdparty/stout/tests/flags_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/flags_tests.cpp index 8045018..c2c6a6a 100644 --- a/3rdparty/libprocess/3rdparty/stout/tests/flags_tests.cpp +++ b/3rdparty/libprocess/3rdparty/stout/tests/flags_tests.cpp @@ -486,6 +486,44 @@ TEST(FlagsTest, Errors) } +TEST(FlagsTest, Validate) +{ + // To provide validation functions. + class ValidatingTestFlags : public TestFlags + { + public: + ValidatingTestFlags() + { + add(&duration, + "duration", + "Duration to test validation", + Seconds(10), + [](const Duration& value) -> Option<Error> { + if (value > Hours(1)) { + return Error("Expected --duration to be less than 1 hour"); + } + return None(); + }); + } + + Duration duration; + }; + + ValidatingTestFlags flags; + + int argc = 2; + char* argv[argc]; + + argv[0] = (char*) "/path/to/program"; + argv[1] = (char*) "--duration=2hrs"; + + Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv); + EXPECT_ERROR(load); + + EXPECT_EQ("Expected --duration to be less than 1 hour", load.error()); +} + + TEST(FlagsTest, UsageMessage) { TestFlags flags;
