File indexing completed on 2025-01-18 09:52:42
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #ifndef BOOST_TEST_UTILS_RUNTIME_PARAMETER_HPP
0016 #define BOOST_TEST_UTILS_RUNTIME_PARAMETER_HPP
0017
0018
0019 #include <boost/test/utils/runtime/fwd.hpp>
0020 #include <boost/test/utils/runtime/modifier.hpp>
0021 #include <boost/test/utils/runtime/argument.hpp>
0022 #include <boost/test/utils/runtime/argument_factory.hpp>
0023
0024
0025 #include <boost/test/utils/class_properties.hpp>
0026 #include <boost/test/utils/foreach.hpp>
0027 #include <boost/test/utils/setcolor.hpp>
0028
0029
0030 #include <boost/function.hpp>
0031 #include <boost/algorithm/cxx11/all_of.hpp>
0032
0033
0034 #include <algorithm>
0035
0036 #include <boost/test/detail/suppress_warnings.hpp>
0037
0038 namespace boost {
0039 namespace runtime {
0040
0041 inline
0042 std::ostream& commandline_pretty_print(
0043 std::ostream& ostr,
0044 std::string const& prefix,
0045 std::string const& to_print) {
0046
0047 const int split_at = 80;
0048
0049 std::string::size_type current = 0;
0050
0051 while(current < to_print.size()) {
0052
0053
0054 std::string::size_type startpos = to_print.find_first_not_of(" \t\n", current);
0055 current += startpos - current;
0056
0057 bool has_more_lines = (current + split_at) < to_print.size();
0058
0059 if(has_more_lines) {
0060 std::string::size_type endpos = to_print.find_last_of(" \t\n", current + split_at);
0061 std::string sub(to_print.substr(current, endpos - current));
0062 ostr << prefix << sub;
0063 ostr << "\n";
0064 current += endpos - current;
0065 }
0066 else
0067 {
0068 ostr << prefix << to_print.substr(current, split_at);
0069 current += split_at;
0070 }
0071 }
0072 return ostr;
0073 }
0074
0075
0076
0077
0078
0079
0080 struct parameter_cla_id {
0081 parameter_cla_id( cstring prefix, cstring tag, cstring value_separator, bool negatable )
0082 : m_prefix( prefix.begin(), prefix.size() )
0083 , m_tag( tag.begin(), tag.size() )
0084 , m_value_separator( value_separator.begin(), value_separator.size() )
0085 , m_negatable( negatable )
0086 {
0087
0088 BOOST_TEST_I_ASSRT( algorithm::all_of( m_prefix.begin(), m_prefix.end(), valid_prefix_char ),
0089 invalid_cla_id() << "Parameter " << m_tag
0090 << " has invalid characters in prefix." );
0091
0092 BOOST_TEST_I_ASSRT( algorithm::all_of( m_tag.begin(), m_tag.end(), valid_name_char ),
0093 invalid_cla_id() << "Parameter " << m_tag
0094 << " has invalid characters in name." );
0095
0096 BOOST_TEST_I_ASSRT( algorithm::all_of( m_value_separator.begin(), m_value_separator.end(), valid_separator_char ),
0097 invalid_cla_id() << "Parameter " << m_tag
0098 << " has invalid characters in value separator." );
0099 }
0100
0101 static bool valid_prefix_char( char c )
0102 {
0103 return c == '-' || c == '/' ;
0104 }
0105 static bool valid_separator_char( char c )
0106 {
0107 return c == '=' || c == ':' || c == ' ' || c == '\0';
0108 }
0109 static bool valid_name_char( char c )
0110 {
0111 return std::isalnum( c ) || c == '+' || c == '_' || c == '?';
0112 }
0113
0114 std::string m_prefix;
0115 std::string m_tag;
0116 std::string m_value_separator;
0117 bool m_negatable;
0118 };
0119
0120 typedef std::vector<parameter_cla_id> param_cla_ids;
0121
0122
0123
0124
0125
0126 cstring const help_prefix("////");
0127
0128 class basic_param {
0129 typedef function<void (cstring)> callback_type;
0130 typedef unit_test::readwrite_property<bool> bool_property;
0131
0132 protected:
0133
0134 template<typename Modifiers>
0135 basic_param( cstring name, bool is_optional, bool is_repeatable, Modifiers const& m )
0136 : p_name( name.begin(), name.end() )
0137 , p_description( nfp::opt_get( m, description, std::string() ) )
0138 , p_help( nfp::opt_get( m, runtime::help, std::string() ) )
0139 , p_env_var( nfp::opt_get( m, env_var, std::string() ) )
0140 , p_value_hint( nfp::opt_get( m, value_hint, std::string() ) )
0141 , p_optional( is_optional )
0142 , p_repeatable( is_repeatable )
0143 , p_has_optional_value( m.has( optional_value ) )
0144 , p_has_default_value( m.has( default_value ) || is_repeatable )
0145 , p_callback( nfp::opt_get( m, callback, callback_type() ) )
0146 {
0147 add_cla_id( help_prefix, name, ":" );
0148 }
0149
0150 public:
0151 BOOST_TEST_DEFAULTED_FUNCTION(virtual ~basic_param(), {})
0152
0153
0154 std::string const p_name;
0155 std::string const p_description;
0156 std::string const p_help;
0157 std::string const p_env_var;
0158 std::string const p_value_hint;
0159 bool const p_optional;
0160 bool const p_repeatable;
0161 bool_property p_has_optional_value;
0162 bool_property p_has_default_value;
0163 callback_type const p_callback;
0164
0165
0166 virtual basic_param_ptr clone() const = 0;
0167
0168
0169 param_cla_ids const& cla_ids() const { return m_cla_ids; }
0170 void add_cla_id( cstring prefix, cstring tag, cstring value_separator )
0171 {
0172 add_cla_id_impl( prefix, tag, value_separator, false, true );
0173 }
0174
0175
0176 virtual void produce_argument( cstring token, bool negative_form, arguments_store& store ) const = 0;
0177 virtual void produce_default( arguments_store& store ) const = 0;
0178
0179
0180 virtual void usage( std::ostream& ostr, cstring negation_prefix_, bool use_color = true )
0181 {
0182 namespace utils = unit_test::utils;
0183 namespace ut_detail = unit_test::ut_detail;
0184
0185
0186 ostr << " ";
0187 {
0188
0189 BOOST_TEST_SCOPE_SETCOLOR( use_color, ostr, term_attr::BRIGHT, term_color::GREEN );
0190 ostr << p_name;
0191 }
0192
0193 ostr << '\n';
0194
0195 if( !p_description.empty() ) {
0196 commandline_pretty_print(ostr, " ", p_description) << '\n';
0197 }
0198
0199 BOOST_TEST_FOREACH( parameter_cla_id const&, id, cla_ids() ) {
0200 if( id.m_prefix == help_prefix )
0201 continue;
0202
0203 ostr << " " << id.m_prefix;
0204
0205 if( id.m_negatable )
0206 cla_name_help( ostr, id.m_tag, negation_prefix_, use_color );
0207 else
0208 cla_name_help( ostr, id.m_tag, "", use_color );
0209
0210 BOOST_TEST_SCOPE_SETCOLOR( use_color, ostr, term_attr::BRIGHT, term_color::YELLOW );
0211 bool optional_value_ = false;
0212
0213 if( p_has_optional_value ) {
0214 optional_value_ = true;
0215 ostr << '[';
0216 }
0217
0218
0219 if( id.m_value_separator.empty() )
0220 ostr << ' ';
0221 else {
0222 ostr << id.m_value_separator;
0223 }
0224
0225 value_help( ostr );
0226
0227 if( optional_value_ )
0228 ostr << ']';
0229
0230 ostr << '\n';
0231 }
0232 }
0233
0234 virtual void help( std::ostream& ostr, cstring negation_prefix_, bool use_color = true )
0235 {
0236 usage( ostr, negation_prefix_, use_color );
0237
0238 if( !p_help.empty() ) {
0239 ostr << '\n';
0240 commandline_pretty_print(ostr, " ", p_help);
0241 }
0242 }
0243
0244 protected:
0245 void add_cla_id_impl( cstring prefix,
0246 cstring tag,
0247 cstring value_separator,
0248 bool negatable,
0249 bool validate_value_separator )
0250 {
0251 BOOST_TEST_I_ASSRT( !tag.is_empty(),
0252 invalid_cla_id() << "Parameter can't have an empty name." );
0253
0254 BOOST_TEST_I_ASSRT( !prefix.is_empty(),
0255 invalid_cla_id() << "Parameter " << tag
0256 << " can't have an empty prefix." );
0257
0258 BOOST_TEST_I_ASSRT( !value_separator.is_empty(),
0259 invalid_cla_id() << "Parameter " << tag
0260 << " can't have an empty value separator." );
0261
0262
0263 value_separator.trim();
0264 BOOST_TEST_I_ASSRT( !validate_value_separator || !value_separator.is_empty() || !p_has_optional_value,
0265 invalid_cla_id() << "Parameter " << tag
0266 << " with optional value attribute can't use space as value separator." );
0267
0268 m_cla_ids.push_back( parameter_cla_id( prefix, tag, value_separator, negatable ) );
0269 }
0270
0271 private:
0272
0273 virtual void cla_name_help( std::ostream& ostr, cstring cla_tag, cstring , bool = true) const
0274 {
0275 ostr << cla_tag;
0276 }
0277 virtual void value_help( std::ostream& ostr ) const
0278 {
0279 if( p_value_hint.empty() )
0280 ostr << "<value>";
0281 else
0282 ostr << p_value_hint;
0283 }
0284
0285
0286 param_cla_ids m_cla_ids;
0287 };
0288
0289
0290
0291
0292
0293 enum args_amount {
0294 OPTIONAL_PARAM,
0295 REQUIRED_PARAM,
0296 REPEATABLE_PARAM
0297 };
0298
0299
0300
0301 template<typename ValueType, args_amount a = runtime::OPTIONAL_PARAM, bool is_enum = false>
0302 class parameter : public basic_param {
0303 public:
0304
0305 #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
0306 template<typename Modifiers=nfp::no_params_type>
0307 parameter( cstring name, Modifiers const& m = nfp::no_params )
0308 #else
0309 template<typename Modifiers>
0310 parameter( cstring name, Modifiers const& m )
0311 #endif
0312 : basic_param( name, a != runtime::REQUIRED_PARAM, a == runtime::REPEATABLE_PARAM, m )
0313 , m_arg_factory( m )
0314 {
0315 BOOST_TEST_I_ASSRT( !m.has( default_value ) || a == runtime::OPTIONAL_PARAM,
0316 invalid_param_spec() << "Parameter " << name
0317 << " is not optional and can't have default_value." );
0318
0319 BOOST_TEST_I_ASSRT( !m.has( optional_value ) || !this->p_repeatable,
0320 invalid_param_spec() << "Parameter " << name
0321 << " is repeatable and can't have optional_value." );
0322 }
0323
0324 private:
0325 basic_param_ptr clone() const BOOST_OVERRIDE
0326 {
0327 return basic_param_ptr( new parameter( *this ) );
0328 }
0329 void produce_argument( cstring token, bool , arguments_store& store ) const BOOST_OVERRIDE
0330 {
0331 m_arg_factory.produce_argument( token, this->p_name, store );
0332 }
0333 void produce_default( arguments_store& store ) const BOOST_OVERRIDE
0334 {
0335 if( !this->p_has_default_value )
0336 return;
0337
0338 m_arg_factory.produce_default( this->p_name, store );
0339 }
0340
0341
0342 typedef argument_factory<ValueType, is_enum, a == runtime::REPEATABLE_PARAM> factory_t;
0343 factory_t m_arg_factory;
0344 };
0345
0346
0347
0348 class option : public basic_param {
0349 public:
0350
0351 #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
0352 template<typename Modifiers=nfp::no_params_type>
0353 option( cstring name, Modifiers const& m = nfp::no_params )
0354 #else
0355 template<typename Modifiers>
0356 option( cstring name, Modifiers const& m )
0357 #endif
0358 : basic_param( name, true, false, nfp::opt_append( nfp::opt_append( m, optional_value = true), default_value = false) )
0359 , m_arg_factory( nfp::opt_append( nfp::opt_append( m, optional_value = true), default_value = false) )
0360 {
0361 }
0362
0363 void add_cla_id( cstring prefix, cstring tag, cstring value_separator, bool negatable = false )
0364 {
0365 add_cla_id_impl( prefix, tag, value_separator, negatable, false );
0366 }
0367
0368 private:
0369 basic_param_ptr clone() const BOOST_OVERRIDE
0370 {
0371 return basic_param_ptr( new option( *this ) );
0372 }
0373
0374 void produce_argument( cstring token, bool negative_form, arguments_store& store ) const BOOST_OVERRIDE
0375 {
0376 if( token.empty() )
0377 store.set( p_name, !negative_form );
0378 else {
0379 BOOST_TEST_I_ASSRT( !negative_form,
0380 format_error( p_name ) << "Can't set value to negative form of the argument." );
0381
0382 m_arg_factory.produce_argument( token, p_name, store );
0383 }
0384 }
0385
0386 void produce_default( arguments_store& store ) const BOOST_OVERRIDE
0387 {
0388 m_arg_factory.produce_default( p_name, store );
0389 }
0390 void cla_name_help( std::ostream& ostr, cstring cla_tag, cstring negation_prefix_, bool use_color = true ) const BOOST_OVERRIDE
0391 {
0392 namespace utils = unit_test::utils;
0393 namespace ut_detail = unit_test::ut_detail;
0394
0395 if( !negation_prefix_.is_empty() ) {
0396 BOOST_TEST_SCOPE_SETCOLOR( use_color, ostr, term_attr::BRIGHT, term_color::YELLOW );
0397 ostr << '[' << negation_prefix_ << ']';
0398 }
0399 ostr << cla_tag;
0400 }
0401 void value_help( std::ostream& ostr ) const BOOST_OVERRIDE
0402 {
0403 if( p_value_hint.empty() )
0404 ostr << "<boolean value>";
0405 else
0406 ostr << p_value_hint;
0407 }
0408
0409
0410 typedef argument_factory<bool, false, false> factory_t;
0411 factory_t m_arg_factory;
0412 };
0413
0414
0415
0416 template<typename EnumType, args_amount a = runtime::OPTIONAL_PARAM>
0417 class enum_parameter : public parameter<EnumType, a, true> {
0418 typedef parameter<EnumType, a, true> base;
0419 public:
0420
0421 #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
0422 template<typename Modifiers=nfp::no_params_type>
0423 enum_parameter( cstring name, Modifiers const& m = nfp::no_params )
0424 #else
0425 template<typename Modifiers>
0426 enum_parameter( cstring name, Modifiers const& m )
0427 #endif
0428 : base( name, m )
0429 {
0430 #ifdef BOOST_TEST_CLA_NEW_API
0431 auto const& values = m[enum_values<EnumType>::value];
0432 auto it = values.begin();
0433 #else
0434 std::vector<std::pair<cstring, EnumType> > const& values = m[enum_values<EnumType>::value];
0435 typename std::vector<std::pair<cstring, EnumType> >::const_iterator it = values.begin();
0436 #endif
0437 while( it != values.end() ) {
0438 m_valid_names.push_back( it->first );
0439 ++it;
0440 }
0441 }
0442
0443 private:
0444 basic_param_ptr clone() const BOOST_OVERRIDE
0445 {
0446 return basic_param_ptr( new enum_parameter( *this ) );
0447 }
0448
0449 void value_help( std::ostream& ostr ) const BOOST_OVERRIDE
0450 {
0451 if( this->p_value_hint.empty() ) {
0452 ostr << "<";
0453 bool first = true;
0454 BOOST_TEST_FOREACH( cstring, name, m_valid_names ) {
0455 if( first )
0456 first = false;
0457 else
0458 ostr << '|';
0459 ostr << name;
0460 }
0461 ostr << ">";
0462 }
0463 else
0464 ostr << this->p_value_hint;
0465 }
0466
0467
0468 std::vector<cstring> m_valid_names;
0469 };
0470
0471
0472
0473
0474
0475
0476 class parameters_store {
0477 struct lg_compare {
0478 bool operator()( cstring lh, cstring rh ) const
0479 {
0480 return std::lexicographical_compare(lh.begin(), lh.end(),
0481 rh.begin(), rh.end());
0482 }
0483 };
0484 public:
0485
0486 typedef std::map<cstring, basic_param_ptr, lg_compare> storage_type;
0487
0488
0489 void add( basic_param const& in )
0490 {
0491 basic_param_ptr p = in.clone();
0492
0493 BOOST_TEST_I_ASSRT( m_parameters.insert( std::make_pair( cstring(p->p_name), p ) ).second,
0494 duplicate_param() << "Parameter " << p->p_name << " is duplicate." );
0495 }
0496
0497
0498 bool is_empty() const { return m_parameters.empty(); }
0499
0500 storage_type const& all() const { return m_parameters; }
0501
0502 bool has( cstring name ) const
0503 {
0504 return m_parameters.find( name ) != m_parameters.end();
0505 }
0506
0507 basic_param_ptr get( cstring name ) const
0508 {
0509 storage_type::const_iterator const& found = m_parameters.find( name );
0510 BOOST_TEST_I_ASSRT( found != m_parameters.end(),
0511 unknown_param() << "Parameter " << name << " is unknown." );
0512
0513 return found->second;
0514 }
0515
0516 private:
0517
0518 storage_type m_parameters;
0519 };
0520
0521 }
0522 }
0523
0524 #include <boost/test/detail/enable_warnings.hpp>
0525
0526 #endif