xref: /freebsd/contrib/kyua/utils/cmdline/options_test.cpp (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 // Copyright 2010 The Kyua Authors.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 //   notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 //   notice, this list of conditions and the following disclaimer in the
12 //   documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 //   may be used to endorse or promote products derived from this software
15 //   without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #include "utils/cmdline/options.hpp"
30 
31 #include <atf-c++.hpp>
32 
33 #include "utils/cmdline/exceptions.hpp"
34 #include "utils/defs.hpp"
35 #include "utils/fs/path.hpp"
36 
37 namespace cmdline = utils::cmdline;
38 
39 namespace {
40 
41 
42 /// Simple string-based option type for testing purposes.
43 class mock_option : public cmdline::base_option {
44 public:
45     /// Constructs a mock option with a short name and a long name.
46     ///
47     ///
48     /// \param short_name_ The short name for the option.
49     /// \param long_name_ The long name for the option.
50     /// \param description_ A user-friendly description for the option.
51     /// \param arg_name_ If not NULL, specifies that the option must receive an
52     ///     argument and specifies the name of such argument for documentation
53     ///     purposes.
54     /// \param default_value_ If not NULL, specifies that the option has a
55     ///     default value for the mandatory argument.
56     mock_option(const char short_name_, const char* long_name_,
57                   const char* description_, const char* arg_name_ = NULL,
58                   const char* default_value_ = NULL) :
59         base_option(short_name_, long_name_, description_, arg_name_,
60                     default_value_) {}
61 
62     /// Constructs a mock option with a long name only.
63     ///
64     /// \param long_name_ The long name for the option.
65     /// \param description_ A user-friendly description for the option.
66     /// \param arg_name_ If not NULL, specifies that the option must receive an
67     ///     argument and specifies the name of such argument for documentation
68     ///     purposes.
69     /// \param default_value_ If not NULL, specifies that the option has a
70     ///     default value for the mandatory argument.
71     mock_option(const char* long_name_,
72                   const char* description_, const char* arg_name_ = NULL,
73                   const char* default_value_ = NULL) :
74         base_option(long_name_, description_, arg_name_, default_value_) {}
75 
76     /// The data type of this option.
77     typedef std::string option_type;
78 
79     /// Ensures that the argument passed to the option is valid.
80     ///
81     /// In this particular mock option, this does not perform any validation.
82     void
83     validate(const std::string& /* str */) const
84     {
85         // Do nothing.
86     }
87 
88     /// Returns the input parameter without any conversion.
89     ///
90     /// \param str The user-provided argument to the option.
91     ///
92     /// \return The same value as provided by the user without conversion.
93     static std::string
94     convert(const std::string& str)
95     {
96         return str;
97     }
98 };
99 
100 
101 }  // anonymous namespace
102 
103 
104 ATF_TEST_CASE_WITHOUT_HEAD(base_option__short_name__no_arg);
105 ATF_TEST_CASE_BODY(base_option__short_name__no_arg)
106 {
107     const mock_option o('f', "force", "Force execution");
108     ATF_REQUIRE(o.has_short_name());
109     ATF_REQUIRE_EQ('f', o.short_name());
110     ATF_REQUIRE_EQ("force", o.long_name());
111     ATF_REQUIRE_EQ("Force execution", o.description());
112     ATF_REQUIRE(!o.needs_arg());
113     ATF_REQUIRE_EQ("-f", o.format_short_name());
114     ATF_REQUIRE_EQ("--force", o.format_long_name());
115 }
116 
117 
118 ATF_TEST_CASE_WITHOUT_HEAD(base_option__short_name__with_arg__no_default);
119 ATF_TEST_CASE_BODY(base_option__short_name__with_arg__no_default)
120 {
121     const mock_option o('c', "conf_file", "Configuration file", "path");
122     ATF_REQUIRE(o.has_short_name());
123     ATF_REQUIRE_EQ('c', o.short_name());
124     ATF_REQUIRE_EQ("conf_file", o.long_name());
125     ATF_REQUIRE_EQ("Configuration file", o.description());
126     ATF_REQUIRE(o.needs_arg());
127     ATF_REQUIRE_EQ("path", o.arg_name());
128     ATF_REQUIRE(!o.has_default_value());
129     ATF_REQUIRE_EQ("-c path", o.format_short_name());
130     ATF_REQUIRE_EQ("--conf_file=path", o.format_long_name());
131 }
132 
133 
134 ATF_TEST_CASE_WITHOUT_HEAD(base_option__short_name__with_arg__with_default);
135 ATF_TEST_CASE_BODY(base_option__short_name__with_arg__with_default)
136 {
137     const mock_option o('c', "conf_file", "Configuration file", "path",
138                         "defpath");
139     ATF_REQUIRE(o.has_short_name());
140     ATF_REQUIRE_EQ('c', o.short_name());
141     ATF_REQUIRE_EQ("conf_file", o.long_name());
142     ATF_REQUIRE_EQ("Configuration file", o.description());
143     ATF_REQUIRE(o.needs_arg());
144     ATF_REQUIRE_EQ("path", o.arg_name());
145     ATF_REQUIRE(o.has_default_value());
146     ATF_REQUIRE_EQ("defpath", o.default_value());
147     ATF_REQUIRE_EQ("-c path", o.format_short_name());
148     ATF_REQUIRE_EQ("--conf_file=path", o.format_long_name());
149 }
150 
151 
152 ATF_TEST_CASE_WITHOUT_HEAD(base_option__long_name__no_arg);
153 ATF_TEST_CASE_BODY(base_option__long_name__no_arg)
154 {
155     const mock_option o("dryrun", "Dry run mode");
156     ATF_REQUIRE(!o.has_short_name());
157     ATF_REQUIRE_EQ("dryrun", o.long_name());
158     ATF_REQUIRE_EQ("Dry run mode", o.description());
159     ATF_REQUIRE(!o.needs_arg());
160     ATF_REQUIRE_EQ("--dryrun", o.format_long_name());
161 }
162 
163 
164 ATF_TEST_CASE_WITHOUT_HEAD(base_option__long_name__with_arg__no_default);
165 ATF_TEST_CASE_BODY(base_option__long_name__with_arg__no_default)
166 {
167     const mock_option o("helper", "Path to helper", "path");
168     ATF_REQUIRE(!o.has_short_name());
169     ATF_REQUIRE_EQ("helper", o.long_name());
170     ATF_REQUIRE_EQ("Path to helper", o.description());
171     ATF_REQUIRE(o.needs_arg());
172     ATF_REQUIRE_EQ("path", o.arg_name());
173     ATF_REQUIRE(!o.has_default_value());
174     ATF_REQUIRE_EQ("--helper=path", o.format_long_name());
175 }
176 
177 
178 ATF_TEST_CASE_WITHOUT_HEAD(base_option__long_name__with_arg__with_default);
179 ATF_TEST_CASE_BODY(base_option__long_name__with_arg__with_default)
180 {
181     const mock_option o("executable", "Executable name", "file", "foo");
182     ATF_REQUIRE(!o.has_short_name());
183     ATF_REQUIRE_EQ("executable", o.long_name());
184     ATF_REQUIRE_EQ("Executable name", o.description());
185     ATF_REQUIRE(o.needs_arg());
186     ATF_REQUIRE_EQ("file", o.arg_name());
187     ATF_REQUIRE(o.has_default_value());
188     ATF_REQUIRE_EQ("foo", o.default_value());
189     ATF_REQUIRE_EQ("--executable=file", o.format_long_name());
190 }
191 
192 
193 ATF_TEST_CASE_WITHOUT_HEAD(bool_option__short_name);
194 ATF_TEST_CASE_BODY(bool_option__short_name)
195 {
196     const cmdline::bool_option o('f', "force", "Force execution");
197     ATF_REQUIRE(o.has_short_name());
198     ATF_REQUIRE_EQ('f', o.short_name());
199     ATF_REQUIRE_EQ("force", o.long_name());
200     ATF_REQUIRE_EQ("Force execution", o.description());
201     ATF_REQUIRE(!o.needs_arg());
202 }
203 
204 
205 ATF_TEST_CASE_WITHOUT_HEAD(bool_option__long_name);
206 ATF_TEST_CASE_BODY(bool_option__long_name)
207 {
208     const cmdline::bool_option o("force", "Force execution");
209     ATF_REQUIRE(!o.has_short_name());
210     ATF_REQUIRE_EQ("force", o.long_name());
211     ATF_REQUIRE_EQ("Force execution", o.description());
212     ATF_REQUIRE(!o.needs_arg());
213 }
214 
215 
216 ATF_TEST_CASE_WITHOUT_HEAD(int_option__short_name);
217 ATF_TEST_CASE_BODY(int_option__short_name)
218 {
219     const cmdline::int_option o('p', "int", "The int", "arg", "value");
220     ATF_REQUIRE(o.has_short_name());
221     ATF_REQUIRE_EQ('p', o.short_name());
222     ATF_REQUIRE_EQ("int", o.long_name());
223     ATF_REQUIRE_EQ("The int", o.description());
224     ATF_REQUIRE(o.needs_arg());
225     ATF_REQUIRE_EQ("arg", o.arg_name());
226     ATF_REQUIRE(o.has_default_value());
227     ATF_REQUIRE_EQ("value", o.default_value());
228 }
229 
230 
231 ATF_TEST_CASE_WITHOUT_HEAD(int_option__long_name);
232 ATF_TEST_CASE_BODY(int_option__long_name)
233 {
234     const cmdline::int_option o("int", "The int", "arg", "value");
235     ATF_REQUIRE(!o.has_short_name());
236     ATF_REQUIRE_EQ("int", o.long_name());
237     ATF_REQUIRE_EQ("The int", o.description());
238     ATF_REQUIRE(o.needs_arg());
239     ATF_REQUIRE_EQ("arg", o.arg_name());
240     ATF_REQUIRE(o.has_default_value());
241     ATF_REQUIRE_EQ("value", o.default_value());
242 }
243 
244 
245 ATF_TEST_CASE_WITHOUT_HEAD(int_option__type);
246 ATF_TEST_CASE_BODY(int_option__type)
247 {
248     const cmdline::int_option o("int", "The int", "arg");
249 
250     o.validate("123");
251     ATF_REQUIRE_EQ(123, cmdline::int_option::convert("123"));
252 
253     o.validate("-567");
254     ATF_REQUIRE_EQ(-567, cmdline::int_option::convert("-567"));
255 
256     ATF_REQUIRE_THROW(cmdline::option_argument_value_error, o.validate(""));
257     ATF_REQUIRE_THROW(cmdline::option_argument_value_error, o.validate("5a"));
258     ATF_REQUIRE_THROW(cmdline::option_argument_value_error, o.validate("a5"));
259     ATF_REQUIRE_THROW(cmdline::option_argument_value_error, o.validate("5 a"));
260     ATF_REQUIRE_THROW(cmdline::option_argument_value_error, o.validate("5.0"));
261 }
262 
263 
264 ATF_TEST_CASE_WITHOUT_HEAD(list_option__short_name);
265 ATF_TEST_CASE_BODY(list_option__short_name)
266 {
267     const cmdline::list_option o('p', "list", "The list", "arg", "value");
268     ATF_REQUIRE(o.has_short_name());
269     ATF_REQUIRE_EQ('p', o.short_name());
270     ATF_REQUIRE_EQ("list", o.long_name());
271     ATF_REQUIRE_EQ("The list", o.description());
272     ATF_REQUIRE(o.needs_arg());
273     ATF_REQUIRE_EQ("arg", o.arg_name());
274     ATF_REQUIRE(o.has_default_value());
275     ATF_REQUIRE_EQ("value", o.default_value());
276 }
277 
278 
279 ATF_TEST_CASE_WITHOUT_HEAD(list_option__long_name);
280 ATF_TEST_CASE_BODY(list_option__long_name)
281 {
282     const cmdline::list_option o("list", "The list", "arg", "value");
283     ATF_REQUIRE(!o.has_short_name());
284     ATF_REQUIRE_EQ("list", o.long_name());
285     ATF_REQUIRE_EQ("The list", o.description());
286     ATF_REQUIRE(o.needs_arg());
287     ATF_REQUIRE_EQ("arg", o.arg_name());
288     ATF_REQUIRE(o.has_default_value());
289     ATF_REQUIRE_EQ("value", o.default_value());
290 }
291 
292 
293 ATF_TEST_CASE_WITHOUT_HEAD(list_option__type);
294 ATF_TEST_CASE_BODY(list_option__type)
295 {
296     const cmdline::list_option o("list", "The list", "arg");
297 
298     o.validate("");
299     {
300         const cmdline::list_option::option_type words =
301             cmdline::list_option::convert("");
302         ATF_REQUIRE(words.empty());
303     }
304 
305     o.validate("foo");
306     {
307         const cmdline::list_option::option_type words =
308             cmdline::list_option::convert("foo");
309         ATF_REQUIRE_EQ(1, words.size());
310         ATF_REQUIRE_EQ("foo", words[0]);
311     }
312 
313     o.validate("foo,bar,baz");
314     {
315         const cmdline::list_option::option_type words =
316             cmdline::list_option::convert("foo,bar,baz");
317         ATF_REQUIRE_EQ(3, words.size());
318         ATF_REQUIRE_EQ("foo", words[0]);
319         ATF_REQUIRE_EQ("bar", words[1]);
320         ATF_REQUIRE_EQ("baz", words[2]);
321     }
322 
323     o.validate("foo,bar,");
324     {
325         const cmdline::list_option::option_type words =
326             cmdline::list_option::convert("foo,bar,");
327         ATF_REQUIRE_EQ(3, words.size());
328         ATF_REQUIRE_EQ("foo", words[0]);
329         ATF_REQUIRE_EQ("bar", words[1]);
330         ATF_REQUIRE_EQ("", words[2]);
331     }
332 
333     o.validate(",foo,bar");
334     {
335         const cmdline::list_option::option_type words =
336             cmdline::list_option::convert(",foo,bar");
337         ATF_REQUIRE_EQ(3, words.size());
338         ATF_REQUIRE_EQ("", words[0]);
339         ATF_REQUIRE_EQ("foo", words[1]);
340         ATF_REQUIRE_EQ("bar", words[2]);
341     }
342 
343     o.validate("foo,,bar");
344     {
345         const cmdline::list_option::option_type words =
346             cmdline::list_option::convert("foo,,bar");
347         ATF_REQUIRE_EQ(3, words.size());
348         ATF_REQUIRE_EQ("foo", words[0]);
349         ATF_REQUIRE_EQ("", words[1]);
350         ATF_REQUIRE_EQ("bar", words[2]);
351     }
352 }
353 
354 
355 ATF_TEST_CASE_WITHOUT_HEAD(path_option__short_name);
356 ATF_TEST_CASE_BODY(path_option__short_name)
357 {
358     const cmdline::path_option o('p', "path", "The path", "arg", "value");
359     ATF_REQUIRE(o.has_short_name());
360     ATF_REQUIRE_EQ('p', o.short_name());
361     ATF_REQUIRE_EQ("path", o.long_name());
362     ATF_REQUIRE_EQ("The path", o.description());
363     ATF_REQUIRE(o.needs_arg());
364     ATF_REQUIRE_EQ("arg", o.arg_name());
365     ATF_REQUIRE(o.has_default_value());
366     ATF_REQUIRE_EQ("value", o.default_value());
367 }
368 
369 
370 ATF_TEST_CASE_WITHOUT_HEAD(path_option__long_name);
371 ATF_TEST_CASE_BODY(path_option__long_name)
372 {
373     const cmdline::path_option o("path", "The path", "arg", "value");
374     ATF_REQUIRE(!o.has_short_name());
375     ATF_REQUIRE_EQ("path", o.long_name());
376     ATF_REQUIRE_EQ("The path", o.description());
377     ATF_REQUIRE(o.needs_arg());
378     ATF_REQUIRE_EQ("arg", o.arg_name());
379     ATF_REQUIRE(o.has_default_value());
380     ATF_REQUIRE_EQ("value", o.default_value());
381 }
382 
383 
384 ATF_TEST_CASE_WITHOUT_HEAD(path_option__type);
385 ATF_TEST_CASE_BODY(path_option__type)
386 {
387     const cmdline::path_option o("path", "The path", "arg");
388 
389     o.validate("/some/path");
390 
391     try {
392         o.validate("");
393         fail("option_argument_value_error not raised");
394     } catch (const cmdline::option_argument_value_error& e) {
395         // Expected; ignore.
396     }
397 
398     const cmdline::path_option::option_type path =
399         cmdline::path_option::convert("/foo/bar");
400     ATF_REQUIRE_EQ("bar", path.leaf_name());  // Ensure valid type.
401 }
402 
403 
404 ATF_TEST_CASE_WITHOUT_HEAD(property_option__short_name);
405 ATF_TEST_CASE_BODY(property_option__short_name)
406 {
407     const cmdline::property_option o('p', "property", "The property", "a=b");
408     ATF_REQUIRE(o.has_short_name());
409     ATF_REQUIRE_EQ('p', o.short_name());
410     ATF_REQUIRE_EQ("property", o.long_name());
411     ATF_REQUIRE_EQ("The property", o.description());
412     ATF_REQUIRE(o.needs_arg());
413     ATF_REQUIRE_EQ("a=b", o.arg_name());
414     ATF_REQUIRE(!o.has_default_value());
415 }
416 
417 
418 ATF_TEST_CASE_WITHOUT_HEAD(property_option__long_name);
419 ATF_TEST_CASE_BODY(property_option__long_name)
420 {
421     const cmdline::property_option o("property", "The property", "a=b");
422     ATF_REQUIRE(!o.has_short_name());
423     ATF_REQUIRE_EQ("property", o.long_name());
424     ATF_REQUIRE_EQ("The property", o.description());
425     ATF_REQUIRE(o.needs_arg());
426     ATF_REQUIRE_EQ("a=b", o.arg_name());
427     ATF_REQUIRE(!o.has_default_value());
428 }
429 
430 
431 ATF_TEST_CASE_WITHOUT_HEAD(property_option__type);
432 ATF_TEST_CASE_BODY(property_option__type)
433 {
434     typedef std::pair< std::string, std::string > string_pair;
435     const cmdline::property_option o("property", "The property", "a=b");
436 
437     o.validate("foo=bar");
438     ATF_REQUIRE(string_pair("foo", "bar") ==
439                 cmdline::property_option::convert("foo=bar"));
440 
441     o.validate(" foo  = bar  baz");
442     ATF_REQUIRE(string_pair(" foo  ", " bar  baz") ==
443                 cmdline::property_option::convert(" foo  = bar  baz"));
444 
445     ATF_REQUIRE_THROW(cmdline::option_argument_value_error, o.validate(""));
446     ATF_REQUIRE_THROW(cmdline::option_argument_value_error, o.validate("="));
447     ATF_REQUIRE_THROW(cmdline::option_argument_value_error, o.validate("a="));
448     ATF_REQUIRE_THROW(cmdline::option_argument_value_error, o.validate("=b"));
449 }
450 
451 
452 ATF_TEST_CASE_WITHOUT_HEAD(string_option__short_name);
453 ATF_TEST_CASE_BODY(string_option__short_name)
454 {
455     const cmdline::string_option o('p', "string", "The string", "arg", "value");
456     ATF_REQUIRE(o.has_short_name());
457     ATF_REQUIRE_EQ('p', o.short_name());
458     ATF_REQUIRE_EQ("string", o.long_name());
459     ATF_REQUIRE_EQ("The string", o.description());
460     ATF_REQUIRE(o.needs_arg());
461     ATF_REQUIRE_EQ("arg", o.arg_name());
462     ATF_REQUIRE(o.has_default_value());
463     ATF_REQUIRE_EQ("value", o.default_value());
464 }
465 
466 
467 ATF_TEST_CASE_WITHOUT_HEAD(string_option__long_name);
468 ATF_TEST_CASE_BODY(string_option__long_name)
469 {
470     const cmdline::string_option o("string", "The string", "arg", "value");
471     ATF_REQUIRE(!o.has_short_name());
472     ATF_REQUIRE_EQ("string", o.long_name());
473     ATF_REQUIRE_EQ("The string", o.description());
474     ATF_REQUIRE(o.needs_arg());
475     ATF_REQUIRE_EQ("arg", o.arg_name());
476     ATF_REQUIRE(o.has_default_value());
477     ATF_REQUIRE_EQ("value", o.default_value());
478 }
479 
480 
481 ATF_TEST_CASE_WITHOUT_HEAD(string_option__type);
482 ATF_TEST_CASE_BODY(string_option__type)
483 {
484     const cmdline::string_option o("string", "The string", "foo");
485 
486     o.validate("");
487     o.validate("some string");
488 
489     const cmdline::string_option::option_type string =
490         cmdline::string_option::convert("foo");
491     ATF_REQUIRE_EQ(3, string.length());  // Ensure valid type.
492 }
493 
494 
495 ATF_INIT_TEST_CASES(tcs)
496 {
497     ATF_ADD_TEST_CASE(tcs, base_option__short_name__no_arg);
498     ATF_ADD_TEST_CASE(tcs, base_option__short_name__with_arg__no_default);
499     ATF_ADD_TEST_CASE(tcs, base_option__short_name__with_arg__with_default);
500     ATF_ADD_TEST_CASE(tcs, base_option__long_name__no_arg);
501     ATF_ADD_TEST_CASE(tcs, base_option__long_name__with_arg__no_default);
502     ATF_ADD_TEST_CASE(tcs, base_option__long_name__with_arg__with_default);
503 
504     ATF_ADD_TEST_CASE(tcs, bool_option__short_name);
505     ATF_ADD_TEST_CASE(tcs, bool_option__long_name);
506 
507     ATF_ADD_TEST_CASE(tcs, int_option__short_name);
508     ATF_ADD_TEST_CASE(tcs, int_option__long_name);
509     ATF_ADD_TEST_CASE(tcs, int_option__type);
510 
511     ATF_ADD_TEST_CASE(tcs, list_option__short_name);
512     ATF_ADD_TEST_CASE(tcs, list_option__long_name);
513     ATF_ADD_TEST_CASE(tcs, list_option__type);
514 
515     ATF_ADD_TEST_CASE(tcs, path_option__short_name);
516     ATF_ADD_TEST_CASE(tcs, path_option__long_name);
517     ATF_ADD_TEST_CASE(tcs, path_option__type);
518 
519     ATF_ADD_TEST_CASE(tcs, property_option__short_name);
520     ATF_ADD_TEST_CASE(tcs, property_option__long_name);
521     ATF_ADD_TEST_CASE(tcs, property_option__type);
522 
523     ATF_ADD_TEST_CASE(tcs, string_option__short_name);
524     ATF_ADD_TEST_CASE(tcs, string_option__long_name);
525     ATF_ADD_TEST_CASE(tcs, string_option__type);
526 }
527