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
37namespace cmdline = utils::cmdline;
38
39namespace {
40
41
42/// Simple string-based option type for testing purposes.
43class mock_option : public cmdline::base_option {
44public:
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
104ATF_TEST_CASE_WITHOUT_HEAD(base_option__short_name__no_arg);
105ATF_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
118ATF_TEST_CASE_WITHOUT_HEAD(base_option__short_name__with_arg__no_default);
119ATF_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
134ATF_TEST_CASE_WITHOUT_HEAD(base_option__short_name__with_arg__with_default);
135ATF_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
152ATF_TEST_CASE_WITHOUT_HEAD(base_option__long_name__no_arg);
153ATF_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
164ATF_TEST_CASE_WITHOUT_HEAD(base_option__long_name__with_arg__no_default);
165ATF_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
178ATF_TEST_CASE_WITHOUT_HEAD(base_option__long_name__with_arg__with_default);
179ATF_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
193ATF_TEST_CASE_WITHOUT_HEAD(bool_option__short_name);
194ATF_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
205ATF_TEST_CASE_WITHOUT_HEAD(bool_option__long_name);
206ATF_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
216ATF_TEST_CASE_WITHOUT_HEAD(int_option__short_name);
217ATF_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
231ATF_TEST_CASE_WITHOUT_HEAD(int_option__long_name);
232ATF_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
245ATF_TEST_CASE_WITHOUT_HEAD(int_option__type);
246ATF_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
264ATF_TEST_CASE_WITHOUT_HEAD(list_option__short_name);
265ATF_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
279ATF_TEST_CASE_WITHOUT_HEAD(list_option__long_name);
280ATF_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
293ATF_TEST_CASE_WITHOUT_HEAD(list_option__type);
294ATF_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
355ATF_TEST_CASE_WITHOUT_HEAD(path_option__short_name);
356ATF_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
370ATF_TEST_CASE_WITHOUT_HEAD(path_option__long_name);
371ATF_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
384ATF_TEST_CASE_WITHOUT_HEAD(path_option__type);
385ATF_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
404ATF_TEST_CASE_WITHOUT_HEAD(property_option__short_name);
405ATF_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
418ATF_TEST_CASE_WITHOUT_HEAD(property_option__long_name);
419ATF_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
431ATF_TEST_CASE_WITHOUT_HEAD(property_option__type);
432ATF_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
452ATF_TEST_CASE_WITHOUT_HEAD(string_option__short_name);
453ATF_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
467ATF_TEST_CASE_WITHOUT_HEAD(string_option__long_name);
468ATF_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
481ATF_TEST_CASE_WITHOUT_HEAD(string_option__type);
482ATF_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
495ATF_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