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/parsers.hpp>
8 #include <boost/program_options/options_description.hpp>
9 #include <boost/program_options/variables_map.hpp>
10 using namespace boost::program_options;
11 // We'll use po::value everywhere to workaround vc6 bug.
12 namespace po = boost::program_options;
13
14 #include <boost/function.hpp>
15 using namespace boost;
16
17 #include <sstream>
18 #include <iostream>
19 using namespace std;
20
21 #if defined(__sun)
22 #include <stdlib.h> // for putenv on solaris
23 #else
24 #include <cstdlib> // for putenv
25 #endif
26
27 #include "minitest.hpp"
28
29 #define TEST_CHECK_THROW(expression, exception, description) \
30 try \
31 { \
32 expression; \
33 BOOST_ERROR(description);\
34 throw 10; \
35 } \
36 catch(exception &) \
37 { \
38 }
39
msp(const string & s1)40 pair<string, vector< vector<string> > > msp(const string& s1)
41 {
42 return std::make_pair(s1, vector< vector<string> >());
43 }
44
45
msp(const string & s1,const string & s2)46 pair<string, vector< vector<string> > > msp(const string& s1, const string& s2)
47 {
48 vector< vector<string> > v(1);
49 v[0].push_back(s2);
50 return std::make_pair(s1, v);
51 }
52
check_value(const option & option,const char * name,const char * value)53 void check_value(const option& option, const char* name, const char* value)
54 {
55 BOOST_CHECK(option.string_key == name);
56 BOOST_REQUIRE(option.value.size() == 1);
57 BOOST_CHECK(option.value.front() == value);
58 }
59
sv(const char * array[],unsigned size)60 vector<string> sv(const char* array[], unsigned size)
61 {
62 vector<string> r;
63 for (unsigned i = 0; i < size; ++i)
64 r.push_back(array[i]);
65 return r;
66 }
67
additional_parser(const std::string &)68 pair<string, string> additional_parser(const std::string&)
69 {
70 return pair<string, string>();
71 }
72
test_command_line()73 void test_command_line()
74 {
75 // The following commented out blocks used to test parsing
76 // command line without syntax specification behaviour.
77 // It is disabled now and probably will never be enabled again:
78 // it is not possible to figure out what command line means without
79 // user's help.
80 #if 0
81 char* cmdline1[] = { "--a", "--b=12", "-f", "-g4", "-", "file" };
82
83 options_and_arguments a1 =
84 parse_command_line(cmdline1,
85 cmdline1 + sizeof(cmdline1)/sizeof(cmdline1[0]));
86
87 BOOST_REQUIRE(a1.options().size() == 4);
88 BOOST_CHECK(a1.options()[0] == msp("a", ""));
89 BOOST_CHECK(a1.options()[1] == msp("b", "12"));
90 BOOST_CHECK(a1.options()[2] == msp("-f", ""));
91 BOOST_CHECK(a1.options()[3] == msp("-g", "4"));
92 BOOST_REQUIRE(a1.arguments().size() == 2);
93 BOOST_CHECK(a1.arguments()[0] == "-");
94 BOOST_CHECK(a1.arguments()[1] == "file");
95
96 char* cmdline2[] = { "--a", "--", "file" };
97
98 options_and_arguments a2 =
99 parse_command_line(cmdline2,
100 cmdline2 + sizeof(cmdline2)/sizeof(cmdline2[0]));
101
102 BOOST_REQUIRE(a2.options().size() == 1);
103 BOOST_CHECK(a2.options()[0] == msp("a", ""));
104 BOOST_CHECK(a2.arguments().size() == 1);
105 BOOST_CHECK(a2.arguments()[0] == "file");
106 #endif
107
108 options_description desc;
109 desc.add_options()
110 ("foo,f", new untyped_value(), "")
111 // Explicit qualification is a workaround for vc6
112 ("bar,b", po::value<std::string>(), "")
113 ("baz", new untyped_value())
114 ("plug*", new untyped_value())
115 ;
116 const char* cmdline3_[] = { "--foo=12", "-f4", "--bar=11", "-b4",
117 "--plug3=10"};
118 vector<string> cmdline3 = sv(cmdline3_,
119 sizeof(cmdline3_)/sizeof(const char*));
120 vector<option> a3 =
121 command_line_parser(cmdline3).options(desc).run().options;
122
123 BOOST_CHECK_EQUAL(a3.size(), 5u);
124
125 check_value(a3[0], "foo", "12");
126 check_value(a3[1], "foo", "4");
127 check_value(a3[2], "bar", "11");
128 check_value(a3[3], "bar", "4");
129 check_value(a3[4], "plug3", "10");
130
131 // Regression test: check that '0' as style is interpreted as
132 // 'default_style'
133 vector<option> a4 =
134 parse_command_line(sizeof(cmdline3_)/sizeof(const char*), cmdline3_,
135 desc, 0, additional_parser).options;
136
137 BOOST_CHECK_EQUAL(a4.size(), 4u);
138 check_value(a4[0], "foo", "4");
139 check_value(a4[1], "bar", "11");
140
141 // Check that we don't crash on empty values of type 'string'
142 const char* cmdline4[] = {"", "--open", ""};
143 options_description desc2;
144 desc2.add_options()
145 ("open", po::value<string>())
146 ;
147 variables_map vm;
148 po::store(po::parse_command_line(sizeof(cmdline4)/sizeof(const char*), const_cast<char**>(cmdline4), desc2), vm);
149
150 const char* cmdline5[] = {"", "-p7", "-o", "1", "2", "3", "-x8"};
151 options_description desc3;
152 desc3.add_options()
153 (",p", po::value<string>())
154 (",o", po::value<string>()->multitoken())
155 (",x", po::value<string>())
156 ;
157 vector<option> a5 =
158 parse_command_line(sizeof(cmdline5)/sizeof(const char*), const_cast<char**>(cmdline5),
159 desc3, 0, additional_parser).options;
160 BOOST_CHECK_EQUAL(a5.size(), 3u);
161 check_value(a5[0], "-p", "7");
162 BOOST_REQUIRE(a5[1].value.size() == 3);
163 BOOST_CHECK_EQUAL(a5[1].string_key, "-o");
164 BOOST_CHECK_EQUAL(a5[1].value[0], "1");
165 BOOST_CHECK_EQUAL(a5[1].value[1], "2");
166 BOOST_CHECK_EQUAL(a5[1].value[2], "3");
167 check_value(a5[2], "-x", "8");
168
169
170 po::options_description desc4( "" );
171 desc4.add_options()
172 ( "multitoken,m",
173 po::value< std::vector< std::string > >()->multitoken(),
174 "values"
175 )
176 ( "file",
177 po::value< std::string >(),
178 "the file to process"
179 )
180 ;
181
182 po::positional_options_description p;
183 p.add( "file", 1 );
184
185 const char* cmdline6[] = {"", "-m", "token1", "token2", "--", "some_file"};
186 vector<option> a6 =
187 command_line_parser(sizeof(cmdline6)/sizeof(const char*), const_cast<char**>(cmdline6)).options(desc4).positional(p)
188 .run().options;
189 BOOST_CHECK_EQUAL(a6.size(), 2u);
190 BOOST_REQUIRE(a6[0].value.size() == 2);
191 BOOST_CHECK_EQUAL(a6[0].string_key, "multitoken");
192 BOOST_CHECK_EQUAL(a6[0].value[0], "token1");
193 BOOST_CHECK_EQUAL(a6[0].value[1], "token2");
194 BOOST_CHECK_EQUAL(a6[1].string_key, "file");
195 BOOST_REQUIRE(a6[1].value.size() == 1);
196 BOOST_CHECK_EQUAL(a6[1].value[0], "some_file");
197 }
198
test_config_file(const char * config_file)199 void test_config_file(const char* config_file)
200 {
201 options_description desc;
202 desc.add_options()
203 ("gv1", new untyped_value)
204 ("gv2", new untyped_value)
205 ("empty_value", new untyped_value)
206 ("plug*", new untyped_value)
207 ("m1.v1", new untyped_value)
208 ("m1.v2", new untyped_value)
209 ("b", bool_switch())
210 ;
211
212 const char content1[] =
213 " gv1 = 0#asd\n"
214 "empty_value = \n"
215 "plug3 = 7\n"
216 "b = true\n"
217 "[m1]\n"
218 "v1 = 1\n"
219 "\n"
220 "v2 = 2\n"
221 ;
222
223 stringstream ss(content1);
224 vector<option> a1 = parse_config_file(ss, desc).options;
225 BOOST_REQUIRE(a1.size() == 6);
226 check_value(a1[0], "gv1", "0");
227 check_value(a1[1], "empty_value", "");
228 check_value(a1[2], "plug3", "7");
229 check_value(a1[3], "b", "true");
230 check_value(a1[4], "m1.v1", "1");
231 check_value(a1[5], "m1.v2", "2");
232
233 // same test, but now options come from file
234 vector<option> a2 = parse_config_file<char>(config_file, desc).options;
235 BOOST_REQUIRE(a2.size() == 6);
236 check_value(a2[0], "gv1", "0");
237 check_value(a2[1], "empty_value", "");
238 check_value(a2[2], "plug3", "7");
239 check_value(a2[3], "b", "true");
240 check_value(a2[4], "m1.v1", "1");
241 check_value(a2[5], "m1.v2", "2");
242 }
243
test_environment()244 void test_environment()
245 {
246 options_description desc;
247 desc.add_options()
248 ("foo", new untyped_value, "")
249 ("bar", new untyped_value, "")
250 ;
251
252 #if defined(_WIN32) && ! defined(__BORLANDC__)
253 _putenv("PO_TEST_FOO=1");
254 #else
255 putenv(const_cast<char*>("PO_TEST_FOO=1"));
256 #endif
257 parsed_options p = parse_environment(desc, "PO_TEST_");
258
259 BOOST_REQUIRE(p.options.size() == 1);
260 BOOST_CHECK(p.options[0].string_key == "foo");
261 BOOST_REQUIRE(p.options[0].value.size() == 1);
262 BOOST_CHECK(p.options[0].value[0] == "1");
263
264 //TODO: since 'bar' does not allow a value, it cannot appear in environemt,
265 // which already has a value.
266 }
267
test_unregistered()268 void test_unregistered()
269 {
270 options_description desc;
271
272 const char* cmdline1_[] = { "--foo=12", "--bar", "1"};
273 vector<string> cmdline1 = sv(cmdline1_,
274 sizeof(cmdline1_)/sizeof(const char*));
275 vector<option> a1 =
276 command_line_parser(cmdline1).options(desc).allow_unregistered().run()
277 .options;
278
279 BOOST_REQUIRE(a1.size() == 3);
280 BOOST_CHECK(a1[0].string_key == "foo");
281 BOOST_CHECK(a1[0].unregistered == true);
282 BOOST_REQUIRE(a1[0].value.size() == 1);
283 BOOST_CHECK(a1[0].value[0] == "12");
284 BOOST_CHECK(a1[1].string_key == "bar");
285 BOOST_CHECK(a1[1].unregistered == true);
286 BOOST_CHECK(a1[2].string_key == "");
287 BOOST_CHECK(a1[2].unregistered == false);
288
289
290 vector<string> a2 = collect_unrecognized(a1, include_positional);
291 BOOST_CHECK(a2[0] == "--foo=12");
292 BOOST_CHECK(a2[1] == "--bar");
293 BOOST_CHECK(a2[2] == "1");
294
295 // Test that storing unregisted options has no effect
296 variables_map vm;
297
298 store(command_line_parser(cmdline1).options(desc).
299 allow_unregistered().run(),
300 vm);
301
302 BOOST_CHECK_EQUAL(vm.size(), 0u);
303
304
305 const char content1[] =
306 "gv1 = 0\n"
307 "[m1]\n"
308 "v1 = 1\n"
309 ;
310
311 stringstream ss(content1);
312 vector<option> a3 = parse_config_file(ss, desc, true).options;
313 BOOST_REQUIRE(a3.size() == 2);
314 cout << "XXX" << a3[0].value.front() << "\n";
315 check_value(a3[0], "gv1", "0");
316 check_value(a3[1], "m1.v1", "1");
317 }
318
main(int,char * av[])319 int main(int, char* av[])
320 {
321 test_command_line();
322 test_config_file(av[1]);
323 test_environment();
324 test_unregistered();
325 return 0;
326 }
327
328