Line data Source code
1 : // (C) Copyright Gennadiy Rozental 2001.
2 : // Distributed under the Boost Software License, Version 1.0.
3 : // (See accompanying file LICENSE_1_0.txt or copy at
4 : // http://www.boost.org/LICENSE_1_0.txt)
5 :
6 : // See http://www.boost.org/libs/test for the library home page.
7 : //
8 : // File : $RCSfile$
9 : //
10 : // Version : $Revision$
11 : //
12 : // Description : formal parameter definition
13 : // ***************************************************************************
14 :
15 : #ifndef BOOST_TEST_UTILS_RUNTIME_PARAMETER_HPP
16 : #define BOOST_TEST_UTILS_RUNTIME_PARAMETER_HPP
17 :
18 : // Boost.Test Runtime parameters
19 : #include <boost/test/utils/runtime/fwd.hpp>
20 : #include <boost/test/utils/runtime/modifier.hpp>
21 : #include <boost/test/utils/runtime/argument.hpp>
22 : #include <boost/test/utils/runtime/argument_factory.hpp>
23 :
24 : // Boost.Test
25 : #include <boost/test/utils/class_properties.hpp>
26 : #include <boost/test/utils/foreach.hpp>
27 : #include <boost/test/utils/setcolor.hpp>
28 :
29 : // Boost
30 : #include <boost/function.hpp>
31 : #include <boost/algorithm/cxx11/all_of.hpp>
32 :
33 : // STL
34 : #include <algorithm>
35 :
36 : #include <boost/test/detail/suppress_warnings.hpp>
37 :
38 : namespace boost {
39 : namespace runtime {
40 :
41 : inline
42 0 : std::ostream& commandline_pretty_print(
43 : std::ostream& ostr,
44 : std::string const& prefix,
45 : std::string const& to_print) {
46 :
47 0 : const int split_at = 80;
48 :
49 0 : std::string::size_type current = 0;
50 :
51 0 : while(current < to_print.size()) {
52 :
53 : // discards spaces at the beginning
54 0 : std::string::size_type startpos = to_print.find_first_not_of(" \t\n", current);
55 0 : current += startpos - current;
56 :
57 0 : bool has_more_lines = (current + split_at) < to_print.size();
58 :
59 0 : if(has_more_lines) {
60 0 : std::string::size_type endpos = to_print.find_last_of(" \t\n", current + split_at);
61 0 : std::string sub(to_print.substr(current, endpos - current));
62 0 : ostr << prefix << sub;
63 0 : ostr << "\n";
64 0 : current += endpos - current;
65 0 : }
66 : else
67 : {
68 0 : ostr << prefix << to_print.substr(current, split_at);
69 0 : current += split_at;
70 : }
71 : }
72 0 : return ostr;
73 0 : }
74 :
75 : // ************************************************************************** //
76 : // ************** runtime::parameter_cla_id ************** //
77 : // ************************************************************************** //
78 : // set of attributes identifying the parameter in the command line
79 :
80 10804 : struct parameter_cla_id {
81 20732 : parameter_cla_id( cstring prefix, cstring tag, cstring value_separator, bool negatable )
82 10366 : : m_prefix( prefix.begin(), prefix.size() )
83 10366 : , m_tag( tag.begin(), tag.size() )
84 10366 : , m_value_separator( value_separator.begin(), value_separator.size() )
85 10366 : , m_negatable( negatable )
86 10366 : {
87 :
88 10366 : BOOST_TEST_I_ASSRT( algorithm::all_of( m_prefix.begin(), m_prefix.end(), valid_prefix_char ),
89 : invalid_cla_id() << "Parameter " << m_tag
90 : << " has invalid characters in prefix." );
91 :
92 10366 : BOOST_TEST_I_ASSRT( algorithm::all_of( m_tag.begin(), m_tag.end(), valid_name_char ),
93 : invalid_cla_id() << "Parameter " << m_tag
94 : << " has invalid characters in name." );
95 :
96 10366 : BOOST_TEST_I_ASSRT( algorithm::all_of( m_value_separator.begin(), m_value_separator.end(), valid_separator_char ),
97 : invalid_cla_id() << "Parameter " << m_tag
98 : << " has invalid characters in value separator." );
99 20732 : }
100 :
101 28178 : static bool valid_prefix_char( char c )
102 : {
103 28178 : return c == '-' || c == '/' ;
104 : }
105 8176 : static bool valid_separator_char( char c )
106 : {
107 8176 : return c == '=' || c == ':' || c == ' ' || c == '\0';
108 : }
109 102200 : static bool valid_name_char( char c )
110 : {
111 102200 : return std::isalnum( c ) || c == '+' || c == '_' || c == '?';
112 : }
113 :
114 : std::string m_prefix;
115 : std::string m_tag;
116 : std::string m_value_separator;
117 : bool m_negatable;
118 : };
119 :
120 : typedef std::vector<parameter_cla_id> param_cla_ids;
121 :
122 : // ************************************************************************** //
123 : // ************** runtime::basic_param ************** //
124 : // ************************************************************************** //
125 :
126 : cstring const help_prefix("////");
127 :
128 4088 : class basic_param {
129 : typedef function<void (cstring)> callback_type;
130 : typedef unit_test::readwrite_property<bool> bool_property;
131 :
132 : protected:
133 : /// Constructor with modifiers
134 : template<typename Modifiers>
135 8176 : basic_param( cstring name, bool is_optional, bool is_repeatable, Modifiers const& m )
136 4088 : : p_name( name.begin(), name.end() )
137 4088 : , p_description( nfp::opt_get( m, description, std::string() ) )
138 4088 : , p_help( nfp::opt_get( m, runtime::help, std::string() ) )
139 4088 : , p_env_var( nfp::opt_get( m, env_var, std::string() ) )
140 4088 : , p_value_hint( nfp::opt_get( m, value_hint, std::string() ) )
141 4088 : , p_optional( is_optional )
142 4088 : , p_repeatable( is_repeatable )
143 4088 : , p_has_optional_value( m.has( optional_value ) )
144 4088 : , p_has_default_value( m.has( default_value ) || is_repeatable )
145 4088 : , p_callback( nfp::opt_get( m, callback, callback_type() ) )
146 4088 : {
147 4088 : add_cla_id( help_prefix, name, ":" );
148 4088 : }
149 :
150 : public:
151 4088 : BOOST_TEST_DEFAULTED_FUNCTION(virtual ~basic_param(), {})
152 :
153 : // Pubic properties
154 : std::string const p_name;
155 : std::string const p_description;
156 : std::string const p_help;
157 : std::string const p_env_var;
158 : std::string const p_value_hint;
159 : bool const p_optional;
160 : bool const p_repeatable;
161 : bool_property p_has_optional_value;
162 : bool_property p_has_default_value;
163 : callback_type const p_callback;
164 :
165 : /// interface for cloning typed parameters
166 : virtual basic_param_ptr clone() const = 0;
167 :
168 : /// Access methods
169 86724 : param_cla_ids const& cla_ids() const { return m_cla_ids; }
170 7446 : void add_cla_id( cstring prefix, cstring tag, cstring value_separator )
171 : {
172 7446 : add_cla_id_impl( prefix, tag, value_separator, false, true );
173 7446 : }
174 :
175 : /// interface for producing argument values for this parameter
176 : virtual void produce_argument( cstring token, bool negative_form, arguments_store& store ) const = 0;
177 : virtual void produce_default( arguments_store& store ) const = 0;
178 :
179 : /// interfaces for help message reporting
180 0 : virtual void usage( std::ostream& ostr, cstring negation_prefix_, bool use_color = true )
181 : {
182 : namespace utils = unit_test::utils;
183 : namespace ut_detail = unit_test::ut_detail;
184 :
185 : //
186 0 : ostr << " ";
187 : {
188 :
189 0 : BOOST_TEST_SCOPE_SETCOLOR( use_color, ostr, term_attr::BRIGHT, term_color::GREEN );
190 0 : ostr << p_name;
191 0 : }
192 :
193 0 : ostr << '\n';
194 :
195 0 : if( !p_description.empty() ) {
196 0 : commandline_pretty_print(ostr, " ", p_description) << '\n';
197 0 : }
198 :
199 0 : BOOST_TEST_FOREACH( parameter_cla_id const&, id, cla_ids() ) {
200 0 : if( id.m_prefix == help_prefix )
201 0 : continue;
202 :
203 0 : ostr << " " << id.m_prefix;
204 :
205 0 : if( id.m_negatable )
206 0 : cla_name_help( ostr, id.m_tag, negation_prefix_, use_color );
207 : else
208 0 : cla_name_help( ostr, id.m_tag, "", use_color );
209 :
210 0 : BOOST_TEST_SCOPE_SETCOLOR( use_color, ostr, term_attr::BRIGHT, term_color::YELLOW );
211 0 : bool optional_value_ = false;
212 :
213 0 : if( p_has_optional_value ) {
214 0 : optional_value_ = true;
215 0 : ostr << '[';
216 0 : }
217 :
218 :
219 0 : if( id.m_value_separator.empty() )
220 0 : ostr << ' ';
221 : else {
222 0 : ostr << id.m_value_separator;
223 : }
224 :
225 0 : value_help( ostr );
226 :
227 0 : if( optional_value_ )
228 0 : ostr << ']';
229 :
230 0 : ostr << '\n';
231 0 : }
232 0 : }
233 :
234 0 : virtual void help( std::ostream& ostr, cstring negation_prefix_, bool use_color = true )
235 : {
236 0 : usage( ostr, negation_prefix_, use_color );
237 :
238 0 : if( !p_help.empty() ) {
239 0 : ostr << '\n';
240 0 : commandline_pretty_print(ostr, " ", p_help);
241 0 : }
242 0 : }
243 :
244 : protected:
245 10366 : void add_cla_id_impl( cstring prefix,
246 : cstring tag,
247 : cstring value_separator,
248 : bool negatable,
249 : bool validate_value_separator )
250 : {
251 10366 : BOOST_TEST_I_ASSRT( !tag.is_empty(),
252 : invalid_cla_id() << "Parameter can't have an empty name." );
253 :
254 10366 : BOOST_TEST_I_ASSRT( !prefix.is_empty(),
255 : invalid_cla_id() << "Parameter " << tag
256 : << " can't have an empty prefix." );
257 :
258 10366 : BOOST_TEST_I_ASSRT( !value_separator.is_empty(),
259 : invalid_cla_id() << "Parameter " << tag
260 : << " can't have an empty value separator." );
261 :
262 : // We trim value separator from all the spaces, so token end will indicate separator
263 10366 : value_separator.trim();
264 10366 : BOOST_TEST_I_ASSRT( !validate_value_separator || !value_separator.is_empty() || !p_has_optional_value,
265 : invalid_cla_id() << "Parameter " << tag
266 : << " with optional value attribute can't use space as value separator." );
267 :
268 10366 : m_cla_ids.push_back( parameter_cla_id( prefix, tag, value_separator, negatable ) );
269 10366 : }
270 :
271 : private:
272 : /// interface for usage/help customization
273 0 : virtual void cla_name_help( std::ostream& ostr, cstring cla_tag, cstring /*negation_prefix_*/, bool /*use_color*/ = true) const
274 : {
275 0 : ostr << cla_tag;
276 0 : }
277 0 : virtual void value_help( std::ostream& ostr ) const
278 : {
279 0 : if( p_value_hint.empty() )
280 0 : ostr << "<value>";
281 : else
282 0 : ostr << p_value_hint;
283 0 : }
284 :
285 : // Data members
286 : param_cla_ids m_cla_ids;
287 : };
288 :
289 : // ************************************************************************** //
290 : // ************** runtime::parameter ************** //
291 : // ************************************************************************** //
292 :
293 : enum args_amount {
294 : OPTIONAL_PARAM, // 0-1
295 : REQUIRED_PARAM, // exactly 1
296 : REPEATABLE_PARAM // 0-N
297 : };
298 :
299 : //____________________________________________________________________________//
300 :
301 : template<typename ValueType, args_amount a = runtime::OPTIONAL_PARAM, bool is_enum = false>
302 1606 : class parameter : public basic_param {
303 : public:
304 : /// Constructor with modifiers
305 : #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
306 : template<typename Modifiers=nfp::no_params_type>
307 3504 : parameter( cstring name, Modifiers const& m = nfp::no_params )
308 : #else
309 : template<typename Modifiers>
310 : parameter( cstring name, Modifiers const& m )
311 : #endif
312 2190 : : basic_param( name, a != runtime::REQUIRED_PARAM, a == runtime::REPEATABLE_PARAM, m )
313 2190 : , m_arg_factory( m )
314 3504 : {
315 2190 : BOOST_TEST_I_ASSRT( !m.has( default_value ) || a == runtime::OPTIONAL_PARAM,
316 : invalid_param_spec() << "Parameter " << name
317 : << " is not optional and can't have default_value." );
318 :
319 2190 : BOOST_TEST_I_ASSRT( !m.has( optional_value ) || !this->p_repeatable,
320 : invalid_param_spec() << "Parameter " << name
321 : << " is repeatable and can't have optional_value." );
322 3504 : }
323 :
324 : private:
325 1314 : basic_param_ptr clone() const BOOST_OVERRIDE
326 : {
327 1314 : return basic_param_ptr( new parameter( *this ) );
328 0 : }
329 292 : void produce_argument( cstring token, bool , arguments_store& store ) const BOOST_OVERRIDE
330 : {
331 292 : m_arg_factory.produce_argument( token, this->p_name, store );
332 292 : }
333 1168 : void produce_default( arguments_store& store ) const BOOST_OVERRIDE
334 : {
335 1168 : if( !this->p_has_default_value )
336 0 : return;
337 :
338 1168 : m_arg_factory.produce_default( this->p_name, store );
339 1168 : }
340 :
341 : // Data members
342 : typedef argument_factory<ValueType, is_enum, a == runtime::REPEATABLE_PARAM> factory_t;
343 : factory_t m_arg_factory;
344 : };
345 :
346 : //____________________________________________________________________________//
347 :
348 : class option : public basic_param {
349 : public:
350 : /// Constructor with modifiers
351 : #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
352 : template<typename Modifiers=nfp::no_params_type>
353 3796 : option( cstring name, Modifiers const& m = nfp::no_params )
354 : #else
355 : template<typename Modifiers>
356 : option( cstring name, Modifiers const& m )
357 : #endif
358 1898 : : basic_param( name, true, false, nfp::opt_append( nfp::opt_append( m, optional_value = true), default_value = false) )
359 1898 : , m_arg_factory( nfp::opt_append( nfp::opt_append( m, optional_value = true), default_value = false) )
360 3796 : {
361 3796 : }
362 :
363 2920 : void add_cla_id( cstring prefix, cstring tag, cstring value_separator, bool negatable = false )
364 : {
365 2920 : add_cla_id_impl( prefix, tag, value_separator, negatable, false );
366 2920 : }
367 :
368 : private:
369 1898 : basic_param_ptr clone() const BOOST_OVERRIDE
370 : {
371 1898 : return basic_param_ptr( new option( *this ) );
372 0 : }
373 :
374 146 : void produce_argument( cstring token, bool negative_form, arguments_store& store ) const BOOST_OVERRIDE
375 : {
376 146 : if( token.empty() )
377 0 : store.set( p_name, !negative_form );
378 : else {
379 146 : BOOST_TEST_I_ASSRT( !negative_form,
380 : format_error( p_name ) << "Can't set value to negative form of the argument." );
381 :
382 146 : m_arg_factory.produce_argument( token, p_name, store );
383 : }
384 146 : }
385 :
386 1752 : void produce_default( arguments_store& store ) const BOOST_OVERRIDE
387 : {
388 1752 : m_arg_factory.produce_default( p_name, store );
389 1752 : }
390 0 : void cla_name_help( std::ostream& ostr, cstring cla_tag, cstring negation_prefix_, bool use_color = true ) const BOOST_OVERRIDE
391 : {
392 : namespace utils = unit_test::utils;
393 : namespace ut_detail = unit_test::ut_detail;
394 :
395 0 : if( !negation_prefix_.is_empty() ) {
396 0 : BOOST_TEST_SCOPE_SETCOLOR( use_color, ostr, term_attr::BRIGHT, term_color::YELLOW );
397 0 : ostr << '[' << negation_prefix_ << ']';
398 0 : }
399 0 : ostr << cla_tag;
400 0 : }
401 0 : void value_help( std::ostream& ostr ) const BOOST_OVERRIDE
402 : {
403 0 : if( p_value_hint.empty() )
404 0 : ostr << "<boolean value>";
405 : else
406 0 : ostr << p_value_hint;
407 0 : }
408 :
409 : // Data members
410 : typedef argument_factory<bool, false, false> factory_t;
411 : factory_t m_arg_factory;
412 : };
413 :
414 : //____________________________________________________________________________//
415 :
416 : template<typename EnumType, args_amount a = runtime::OPTIONAL_PARAM>
417 876 : class enum_parameter : public parameter<EnumType, a, true> {
418 : typedef parameter<EnumType, a, true> base;
419 : public:
420 : /// Constructor with modifiers
421 : #ifndef BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS
422 : template<typename Modifiers=nfp::no_params_type>
423 2628 : enum_parameter( cstring name, Modifiers const& m = nfp::no_params )
424 : #else
425 : template<typename Modifiers>
426 : enum_parameter( cstring name, Modifiers const& m )
427 : #endif
428 876 : : base( name, m )
429 1752 : {
430 : #ifdef BOOST_TEST_CLA_NEW_API
431 876 : auto const& values = m[enum_values<EnumType>::value];
432 876 : auto it = values.begin();
433 : #else
434 : std::vector<std::pair<cstring, EnumType> > const& values = m[enum_values<EnumType>::value];
435 : typename std::vector<std::pair<cstring, EnumType> >::const_iterator it = values.begin();
436 : #endif
437 4818 : while( it != values.end() ) {
438 3942 : m_valid_names.push_back( it->first );
439 3942 : ++it;
440 : }
441 1752 : }
442 :
443 : private:
444 876 : basic_param_ptr clone() const BOOST_OVERRIDE
445 : {
446 876 : return basic_param_ptr( new enum_parameter( *this ) );
447 0 : }
448 :
449 0 : void value_help( std::ostream& ostr ) const BOOST_OVERRIDE
450 : {
451 0 : if( this->p_value_hint.empty() ) {
452 0 : ostr << "<";
453 0 : bool first = true;
454 0 : BOOST_TEST_FOREACH( cstring, name, m_valid_names ) {
455 0 : if( first )
456 0 : first = false;
457 : else
458 0 : ostr << '|';
459 0 : ostr << name;
460 0 : }
461 0 : ostr << ">";
462 0 : }
463 : else
464 0 : ostr << this->p_value_hint;
465 0 : }
466 :
467 : // Data members
468 : std::vector<cstring> m_valid_names;
469 : };
470 :
471 :
472 : // ************************************************************************** //
473 : // ************** runtime::parameters_store ************** //
474 : // ************************************************************************** //
475 :
476 : class parameters_store {
477 : struct lg_compare {
478 37960 : bool operator()( cstring lh, cstring rh ) const
479 : {
480 75920 : return std::lexicographical_compare(lh.begin(), lh.end(),
481 37960 : rh.begin(), rh.end());
482 : }
483 : };
484 : public:
485 :
486 : typedef std::map<cstring, basic_param_ptr, lg_compare> storage_type;
487 :
488 : /// Adds parameter into the persistent store
489 4088 : void add( basic_param const& in )
490 : {
491 4088 : basic_param_ptr p = in.clone();
492 :
493 4088 : BOOST_TEST_I_ASSRT( m_parameters.insert( std::make_pair( cstring(p->p_name), p ) ).second,
494 : duplicate_param() << "Parameter " << p->p_name << " is duplicate." );
495 4088 : }
496 :
497 : /// Returns true if there is no parameters registered
498 146 : bool is_empty() const { return m_parameters.empty(); }
499 : /// Returns map of all the registered parameter
500 76212 : storage_type const& all() const { return m_parameters; }
501 : /// Returns true if parameter with specified name is registered
502 : bool has( cstring name ) const
503 : {
504 : return m_parameters.find( name ) != m_parameters.end();
505 : }
506 : /// Returns map of all the registered parameter
507 : basic_param_ptr get( cstring name ) const
508 : {
509 : storage_type::const_iterator const& found = m_parameters.find( name );
510 : BOOST_TEST_I_ASSRT( found != m_parameters.end(),
511 : unknown_param() << "Parameter " << name << " is unknown." );
512 :
513 : return found->second;
514 : }
515 :
516 : private:
517 : // Data members
518 : storage_type m_parameters;
519 : };
520 :
521 : } // namespace runtime
522 : } // namespace boost
523 :
524 : #include <boost/test/detail/enable_warnings.hpp>
525 :
526 : #endif // BOOST_TEST_UTILS_RUNTIME_PARAMETER_HPP
|