1 | // Copyright Vladimir Prus 2002-2004. |
2 | // Distributed under the Boost Software License, Version 1.0. |
3 | // (See accompanying file LICENSE_1_0.txt |
4 | // or copy at http://www.boost.org/LICENSE_1_0.txt) |
5 | |
6 | |
7 | #include <boost/program_options/options_description.hpp> |
8 | using namespace boost::program_options; |
9 | |
10 | #include <boost/function.hpp> |
11 | using namespace boost; |
12 | |
13 | #include <utility> |
14 | #include <string> |
15 | #include <sstream> |
16 | using namespace std; |
17 | |
18 | #include "minitest.hpp" |
19 | |
20 | void test_type() |
21 | { |
22 | options_description desc; |
23 | desc.add_options() |
24 | ("foo" , value<int>(), "" ) |
25 | ("bar" , value<string>(), "" ) |
26 | ; |
27 | |
28 | #ifndef BOOST_NO_RTTI |
29 | const typed_value_base* b = dynamic_cast<const typed_value_base*> |
30 | (desc.find(name: "foo" , approx: false).semantic().get()); |
31 | BOOST_CHECK(b); |
32 | BOOST_CHECK(b->value_type() == typeid(int)); |
33 | |
34 | const typed_value_base* b2 = dynamic_cast<const typed_value_base*> |
35 | (desc.find(name: "bar" , approx: false).semantic().get()); |
36 | BOOST_CHECK(b2); |
37 | BOOST_CHECK(b2->value_type() == typeid(string)); |
38 | #endif |
39 | } |
40 | |
41 | void test_approximation() |
42 | { |
43 | options_description desc; |
44 | desc.add_options() |
45 | ("foo" , new untyped_value()) |
46 | ("fee" , new untyped_value()) |
47 | ("baz" , new untyped_value()) |
48 | ("all-chroots" , new untyped_value()) |
49 | ("all-sessions" , new untyped_value()) |
50 | ("all" , new untyped_value()) |
51 | ; |
52 | |
53 | BOOST_CHECK_EQUAL(desc.find("fo" , true).long_name(), "foo" ); |
54 | |
55 | BOOST_CHECK_EQUAL(desc.find("all" , true).long_name(), "all" ); |
56 | BOOST_CHECK_EQUAL(desc.find("all-ch" , true).long_name(), "all-chroots" ); |
57 | |
58 | options_description desc2; |
59 | desc2.add_options() |
60 | ("help" , "display this message" ) |
61 | ("config" , value<string>(), "config file name" ) |
62 | ("config-value" , value<string>(), "single config value" ) |
63 | ; |
64 | |
65 | BOOST_CHECK_EQUAL(desc2.find("config" , true).long_name(), "config" ); |
66 | BOOST_CHECK_EQUAL(desc2.find("config-value" , true).long_name(), |
67 | "config-value" ); |
68 | |
69 | |
70 | // BOOST_CHECK(desc.count_approx("foo") == 1); |
71 | // set<string> a = desc.approximations("f"); |
72 | // BOOST_CHECK(a.size() == 2); |
73 | // BOOST_CHECK(*a.begin() == "fee"); |
74 | // BOOST_CHECK(*(++a.begin()) == "foo"); |
75 | } |
76 | |
77 | void test_approximation_with_multiname_options() |
78 | { |
79 | options_description desc; |
80 | desc.add_options() |
81 | ("foo" , new untyped_value()) |
82 | ("fee" , new untyped_value()) |
83 | ("fe,baz" , new untyped_value()) |
84 | ("chroots,all-chroots" , new untyped_value()) |
85 | ("sessions,all-sessions" , new untyped_value()) |
86 | ("everything,all" , new untyped_value()) |
87 | ("qux,fo" , new untyped_value()) |
88 | ; |
89 | |
90 | BOOST_CHECK_EQUAL(desc.find("fo" , true).long_name(), "qux" ); |
91 | |
92 | BOOST_CHECK_EQUAL(desc.find("all" , true).long_name(), "everything" ); |
93 | BOOST_CHECK_EQUAL(desc.find("all-ch" , true).long_name(), "chroots" ); |
94 | |
95 | BOOST_CHECK_EQUAL(desc.find("foo" , false, false, false).long_names().second, 1u); |
96 | BOOST_CHECK_EQUAL(desc.find("foo" , false, false, false).long_names().first[0], "foo" ); |
97 | |
98 | BOOST_CHECK_EQUAL(desc.find("fe" , false, false, false).long_names().second, 2u); |
99 | BOOST_CHECK_EQUAL(desc.find("fe" , false, false, false).long_names().first[0], "fe" ); |
100 | BOOST_CHECK_EQUAL(desc.find("baz" , false, false, false).long_names().first[1], "baz" ); |
101 | |
102 | BOOST_CHECK_EQUAL(desc.find("baz" , false, false, false).long_names().second, 2u); |
103 | BOOST_CHECK_EQUAL(desc.find("baz" , false, false, false).long_names().first[0], "fizbaz" ); |
104 | BOOST_CHECK_EQUAL(desc.find("baz" , false, false, false).long_names().first[1], "baz" ); |
105 | } |
106 | |
107 | void test_long_names_for_option_description() |
108 | { |
109 | options_description desc; |
110 | desc.add_options() |
111 | ("foo" , new untyped_value()) |
112 | ("fe,baz" , new untyped_value()) |
113 | ("chroots,all-chroots" , new untyped_value()) |
114 | ("sessions,all-sessions" , new untyped_value()) |
115 | ("everything,all" , new untyped_value()) |
116 | ("qux,fo,q" , new untyped_value()) |
117 | ; |
118 | |
119 | BOOST_CHECK_EQUAL(desc.find("foo" , false, false, false).long_names().second, 1u); |
120 | BOOST_CHECK_EQUAL(desc.find("foo" , false, false, false).long_names().first[0], "foo" ); |
121 | |
122 | BOOST_CHECK_EQUAL(desc.find("fe" , false, false, false).long_names().second, 2u); |
123 | BOOST_CHECK_EQUAL(desc.find("fe" , false, false, false).long_names().first[0], "fe" ); |
124 | BOOST_CHECK_EQUAL(desc.find("baz" , false, false, false).long_names().first[1], "baz" ); |
125 | |
126 | BOOST_CHECK_EQUAL(desc.find("qux" , false, false, false).long_names().second, 2u); |
127 | BOOST_CHECK_EQUAL(desc.find("qux" , false, false, false).long_names().first[0], "qux" ); |
128 | BOOST_CHECK_EQUAL(desc.find("qux" , false, false, false).long_names().first[1], "fo" ); |
129 | } |
130 | |
131 | |
132 | void test_formatting() |
133 | { |
134 | // Long option descriptions used to crash on MSVC-8.0. |
135 | options_description desc; |
136 | desc.add_options()( |
137 | "test" , new untyped_value(), |
138 | "foo foo foo foo foo foo foo foo foo foo foo foo foo foo" |
139 | "foo foo foo foo foo foo foo foo foo foo foo foo foo foo" |
140 | "foo foo foo foo foo foo foo foo foo foo foo foo foo foo" |
141 | "foo foo foo foo foo foo foo foo foo foo foo foo foo foo" ) |
142 | ("list" , new untyped_value(), |
143 | "a list:\n \t" |
144 | "item1, item2, item3, item4, item5, item6, item7, item8, item9, " |
145 | "item10, item11, item12, item13, item14, item15, item16, item17, item18" ) |
146 | ("well_formated" , new untyped_value(), |
147 | "As you can see this is a very well formatted option description.\n" |
148 | "You can do this for example:\n\n" |
149 | "Values:\n" |
150 | " Value1: \tdoes this and that, bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla\n" |
151 | " Value2: \tdoes something else, bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla\n\n" |
152 | " This paragraph has a first line indent only, bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla" ) |
153 | ; |
154 | |
155 | stringstream ss; |
156 | ss << desc; |
157 | BOOST_CHECK_EQUAL(ss.str(), |
158 | " --test arg foo foo foo foo foo foo foo foo foo foo foo foo foo \n" |
159 | " foofoo foo foo foo foo foo foo foo foo foo foo foo foo \n" |
160 | " foofoo foo foo foo foo foo foo foo foo foo foo foo foo \n" |
161 | " foofoo foo foo foo foo foo foo foo foo foo foo foo foo \n" |
162 | " foo\n" |
163 | " --list arg a list:\n" |
164 | " item1, item2, item3, item4, item5, item6, item7, \n" |
165 | " item8, item9, item10, item11, item12, item13, \n" |
166 | " item14, item15, item16, item17, item18\n" |
167 | " --well_formated arg As you can see this is a very well formatted option \n" |
168 | " description.\n" |
169 | " You can do this for example:\n" |
170 | " \n" |
171 | " Values:\n" |
172 | " Value1: does this and that, bla bla bla bla bla bla \n" |
173 | " bla bla bla bla bla bla bla bla bla\n" |
174 | " Value2: does something else, bla bla bla bla bla bla \n" |
175 | " bla bla bla bla bla bla bla bla bla\n" |
176 | " \n" |
177 | " This paragraph has a first line indent only, bla \n" |
178 | " bla bla bla bla bla bla bla bla bla bla bla bla bla bla\n" |
179 | ); |
180 | } |
181 | |
182 | void test_multiname_option_formatting() |
183 | { |
184 | options_description desc; |
185 | desc.add_options() |
186 | ("foo,bar" , new untyped_value(), "a multiple-name option" ) |
187 | ; |
188 | |
189 | stringstream ss; |
190 | ss << desc; |
191 | BOOST_CHECK_EQUAL(ss.str(), |
192 | " --foo arg a multiple-name option\n" |
193 | ); |
194 | } |
195 | |
196 | |
197 | void test_formatting_description_length() |
198 | { |
199 | { |
200 | options_description desc("" , |
201 | options_description::m_default_line_length, |
202 | options_description::m_default_line_length / 2U); |
203 | desc.add_options() |
204 | ("an-option-that-sets-the-max" , new untyped_value(), // > 40 available for desc |
205 | "this description sits on the same line, but wrapping should still work correctly" ) |
206 | ("a-long-option-that-would-leave-very-little-space-for-description" , new untyped_value(), |
207 | "the description of the long opt, but placed on the next line\n" |
208 | " \talso ensure that the tabulation works correctly when a" |
209 | " description size has been set" ); |
210 | |
211 | stringstream ss; |
212 | ss << desc; |
213 | BOOST_CHECK_EQUAL(ss.str(), |
214 | " --an-option-that-sets-the-max arg this description sits on the same line,\n" |
215 | " but wrapping should still work \n" |
216 | " correctly\n" |
217 | " --a-long-option-that-would-leave-very-little-space-for-description arg\n" |
218 | " the description of the long opt, but \n" |
219 | " placed on the next line\n" |
220 | " also ensure that the tabulation \n" |
221 | " works correctly when a description \n" |
222 | " size has been set\n" ); |
223 | } |
224 | { |
225 | // the default behaviour reserves 23 (+1 space) characters for the |
226 | // option column; this shows that the min_description_length does not |
227 | // breach that. |
228 | options_description desc("" , |
229 | options_description::m_default_line_length, |
230 | options_description::m_default_line_length - 10U); // leaves < 23 (default option space) |
231 | desc.add_options() |
232 | ("an-option-that-encroaches-description" , new untyped_value(), |
233 | "this description should always be placed on the next line, and wrapping should continue as normal" ); |
234 | |
235 | stringstream ss; |
236 | ss << desc; |
237 | BOOST_CHECK_EQUAL(ss.str(), |
238 | " --an-option-that-encroaches-description arg\n" |
239 | //123456789_123456789_ |
240 | " this description should always be placed on the next line, and \n" |
241 | " wrapping should continue as normal\n" ); |
242 | } |
243 | } |
244 | |
245 | void test_long_default_value() |
246 | { |
247 | options_description desc; |
248 | desc.add_options() |
249 | ("cfgfile,c" , |
250 | value<string>()->default_value(v: "/usr/local/etc/myprogramXXXXXXXXX/configuration.conf" ), |
251 | "the configfile" ) |
252 | ; |
253 | |
254 | stringstream ss; |
255 | ss << desc; |
256 | BOOST_CHECK_EQUAL(ss.str(), |
257 | " -c [ --cfgfile ] arg (=/usr/local/etc/myprogramXXXXXXXXX/configuration.conf)\n" |
258 | " the configfile\n" |
259 | ); |
260 | } |
261 | |
262 | void test_word_wrapping() |
263 | { |
264 | options_description desc("Supported options" ); |
265 | desc.add_options() |
266 | ("help" , "this is a sufficiently long text to require word-wrapping" ) |
267 | ("prefix" , value<string>()->default_value(v: "/h/proj/tmp/dispatch" ), "root path of the dispatch installation" ) |
268 | ("opt1" , "this_is_a_sufficiently_long_text_to_require_word-wrapping_but_cannot_be_wrapped" ) |
269 | ("opt2" , "this_is_a_sufficiently long_text_to_require_word-wrapping" ) |
270 | ("opt3" , "this_is_a sufficiently_long_text_to_require_word-wrapping_but_will_not_be_wrapped" ) |
271 | ; |
272 | stringstream ss; |
273 | ss << desc; |
274 | BOOST_CHECK_EQUAL(ss.str(), |
275 | "Supported options:\n" |
276 | " --help this is a sufficiently long text to \n" |
277 | " require word-wrapping\n" |
278 | " --prefix arg (=/h/proj/tmp/dispatch) root path of the dispatch installation\n" |
279 | " --opt1 this_is_a_sufficiently_long_text_to_requ\n" |
280 | " ire_word-wrapping_but_cannot_be_wrapped\n" |
281 | " --opt2 this_is_a_sufficiently \n" |
282 | " long_text_to_require_word-wrapping\n" |
283 | " --opt3 this_is_a sufficiently_long_text_to_requ\n" |
284 | " ire_word-wrapping_but_will_not_be_wrappe\n" |
285 | " d\n" |
286 | ); |
287 | } |
288 | |
289 | void test_default_values() |
290 | { |
291 | options_description desc("Supported options" ); |
292 | desc.add_options() |
293 | ("maxlength" , value<double>()->default_value(v: .1, textual: "0.1" ), "Maximum edge length to keep." ) |
294 | ; |
295 | stringstream ss; |
296 | ss << desc; |
297 | BOOST_CHECK_EQUAL(ss.str(), |
298 | "Supported options:\n" |
299 | " --maxlength arg (=0.1) Maximum edge length to keep.\n" |
300 | ); |
301 | } |
302 | |
303 | void test_value_name() |
304 | { |
305 | options_description desc("Supported options" ); |
306 | desc.add_options() |
307 | ("include" , value<string>()->value_name(name: "directory" ), "Search for headers in 'directory'." ) |
308 | ; |
309 | |
310 | stringstream ss; |
311 | ss << desc; |
312 | BOOST_CHECK_EQUAL(ss.str(), |
313 | "Supported options:\n" |
314 | " --include directory Search for headers in 'directory'.\n" |
315 | ); |
316 | } |
317 | |
318 | void test_multiname_key_and_switch_selection() |
319 | { |
320 | // cases: |
321 | // foo,f -> f |
322 | // foo, c -> c |
323 | // foo,f,g -> g |
324 | // f,g,h -> h |
325 | // f,foo throws |
326 | // foo,bar -> no switch |
327 | // foo,f,bar -> no switch |
328 | |
329 | // what about empty strings - consecutive ,'s ? |
330 | } |
331 | |
332 | int main(int, char* []) |
333 | { |
334 | test_type(); |
335 | test_approximation(); |
336 | test_long_names_for_option_description(); |
337 | test_formatting(); |
338 | test_multiname_key_and_switch_selection(); |
339 | test_multiname_option_formatting(); |
340 | test_formatting_description_length(); |
341 | test_long_default_value(); |
342 | test_word_wrapping(); |
343 | test_default_values(); |
344 | test_value_name(); |
345 | return 0; |
346 | } |
347 | |
348 | |