1*6b3a42afSjmmv // Copyright 2010 Google Inc.
2*6b3a42afSjmmv // All rights reserved.
3*6b3a42afSjmmv //
4*6b3a42afSjmmv // Redistribution and use in source and binary forms, with or without
5*6b3a42afSjmmv // modification, are permitted provided that the following conditions are
6*6b3a42afSjmmv // met:
7*6b3a42afSjmmv //
8*6b3a42afSjmmv // * Redistributions of source code must retain the above copyright
9*6b3a42afSjmmv // notice, this list of conditions and the following disclaimer.
10*6b3a42afSjmmv // * Redistributions in binary form must reproduce the above copyright
11*6b3a42afSjmmv // notice, this list of conditions and the following disclaimer in the
12*6b3a42afSjmmv // documentation and/or other materials provided with the distribution.
13*6b3a42afSjmmv // * Neither the name of Google Inc. nor the names of its contributors
14*6b3a42afSjmmv // may be used to endorse or promote products derived from this software
15*6b3a42afSjmmv // without specific prior written permission.
16*6b3a42afSjmmv //
17*6b3a42afSjmmv // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*6b3a42afSjmmv // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*6b3a42afSjmmv // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*6b3a42afSjmmv // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*6b3a42afSjmmv // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*6b3a42afSjmmv // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*6b3a42afSjmmv // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*6b3a42afSjmmv // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*6b3a42afSjmmv // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*6b3a42afSjmmv // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*6b3a42afSjmmv // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*6b3a42afSjmmv
29*6b3a42afSjmmv #include "utils/cmdline/parser.ipp"
30*6b3a42afSjmmv
31*6b3a42afSjmmv #if defined(HAVE_CONFIG_H)
32*6b3a42afSjmmv #include "config.h"
33*6b3a42afSjmmv #endif
34*6b3a42afSjmmv
35*6b3a42afSjmmv extern "C" {
36*6b3a42afSjmmv #include <fcntl.h>
37*6b3a42afSjmmv #include <getopt.h>
38*6b3a42afSjmmv #include <unistd.h>
39*6b3a42afSjmmv }
40*6b3a42afSjmmv
41*6b3a42afSjmmv #include <cstdlib>
42*6b3a42afSjmmv #include <cstring>
43*6b3a42afSjmmv #include <fstream>
44*6b3a42afSjmmv #include <iostream>
45*6b3a42afSjmmv #include <string>
46*6b3a42afSjmmv #include <utility>
47*6b3a42afSjmmv
48*6b3a42afSjmmv #include <atf-c++.hpp>
49*6b3a42afSjmmv
50*6b3a42afSjmmv #include "utils/cmdline/exceptions.hpp"
51*6b3a42afSjmmv #include "utils/cmdline/options.hpp"
52*6b3a42afSjmmv #include "utils/format/macros.hpp"
53*6b3a42afSjmmv #include "utils/sanity.hpp"
54*6b3a42afSjmmv
55*6b3a42afSjmmv namespace cmdline = utils::cmdline;
56*6b3a42afSjmmv
57*6b3a42afSjmmv using cmdline::base_option;
58*6b3a42afSjmmv using cmdline::bool_option;
59*6b3a42afSjmmv using cmdline::int_option;
60*6b3a42afSjmmv using cmdline::parse;
61*6b3a42afSjmmv using cmdline::parsed_cmdline;
62*6b3a42afSjmmv using cmdline::string_option;
63*6b3a42afSjmmv
64*6b3a42afSjmmv
65*6b3a42afSjmmv namespace {
66*6b3a42afSjmmv
67*6b3a42afSjmmv
68*6b3a42afSjmmv /// Mock option type to check the validate and convert methods sequence.
69*6b3a42afSjmmv ///
70*6b3a42afSjmmv /// Instances of this option accept a string argument that must be either "zero"
71*6b3a42afSjmmv /// or "one". These are validated and converted to integers.
72*6b3a42afSjmmv class mock_option : public base_option {
73*6b3a42afSjmmv public:
74*6b3a42afSjmmv /// Constructs the new option.
75*6b3a42afSjmmv ///
76*6b3a42afSjmmv /// \param long_name_ The long name for the option. All other option
77*6b3a42afSjmmv /// properties are irrelevant for the tests using this, so they are set
78*6b3a42afSjmmv /// to arbitrary values.
mock_option(const char * long_name_)79*6b3a42afSjmmv mock_option(const char* long_name_) :
80*6b3a42afSjmmv base_option(long_name_, "Irrelevant description", "arg")
81*6b3a42afSjmmv {
82*6b3a42afSjmmv }
83*6b3a42afSjmmv
84*6b3a42afSjmmv /// The type of the argument of this option.
85*6b3a42afSjmmv typedef int option_type;
86*6b3a42afSjmmv
87*6b3a42afSjmmv /// Checks that the user-provided option is valid.
88*6b3a42afSjmmv ///
89*6b3a42afSjmmv /// \param str The user argument; must be "zero" or "one".
90*6b3a42afSjmmv ///
91*6b3a42afSjmmv /// \throw cmdline::option_argument_value_error If str is not valid.
92*6b3a42afSjmmv void
validate(const std::string & str) const93*6b3a42afSjmmv validate(const std::string& str) const
94*6b3a42afSjmmv {
95*6b3a42afSjmmv if (str != "zero" && str != "one")
96*6b3a42afSjmmv throw cmdline::option_argument_value_error(F("--%s") % long_name(),
97*6b3a42afSjmmv str, "Unknown value");
98*6b3a42afSjmmv }
99*6b3a42afSjmmv
100*6b3a42afSjmmv /// Converts the user-provided argument to our native integer type.
101*6b3a42afSjmmv ///
102*6b3a42afSjmmv /// \param str The user argument; must be "zero" or "one".
103*6b3a42afSjmmv ///
104*6b3a42afSjmmv /// \return 0 if the input is "zero", or 1 if the input is "one".
105*6b3a42afSjmmv ///
106*6b3a42afSjmmv /// \throw std::runtime_error If str is not valid. In real life, this
107*6b3a42afSjmmv /// should be a precondition because validate() has already ensured that
108*6b3a42afSjmmv /// the values passed to convert() are correct. However, we raise an
109*6b3a42afSjmmv /// exception here because we are actually validating that this code
110*6b3a42afSjmmv /// sequence holds true.
111*6b3a42afSjmmv static int
convert(const std::string & str)112*6b3a42afSjmmv convert(const std::string& str)
113*6b3a42afSjmmv {
114*6b3a42afSjmmv if (str == "zero")
115*6b3a42afSjmmv return 0;
116*6b3a42afSjmmv else if (str == "one")
117*6b3a42afSjmmv return 1;
118*6b3a42afSjmmv else {
119*6b3a42afSjmmv // This would generally be an assertion but, given that this is
120*6b3a42afSjmmv // test code, we want to catch any errors regardless of how the
121*6b3a42afSjmmv // binary is built.
122*6b3a42afSjmmv throw std::runtime_error("Value not validated properly.");
123*6b3a42afSjmmv }
124*6b3a42afSjmmv }
125*6b3a42afSjmmv };
126*6b3a42afSjmmv
127*6b3a42afSjmmv
128*6b3a42afSjmmv /// Redirects stdout and stderr to a file.
129*6b3a42afSjmmv ///
130*6b3a42afSjmmv /// This fails the test case in case of any error.
131*6b3a42afSjmmv ///
132*6b3a42afSjmmv /// \param file The name of the file to redirect stdout and stderr to.
133*6b3a42afSjmmv ///
134*6b3a42afSjmmv /// \return A copy of the old stdout and stderr file descriptors.
135*6b3a42afSjmmv static std::pair< int, int >
mock_stdfds(const char * file)136*6b3a42afSjmmv mock_stdfds(const char* file)
137*6b3a42afSjmmv {
138*6b3a42afSjmmv std::cout.flush();
139*6b3a42afSjmmv std::cerr.flush();
140*6b3a42afSjmmv
141*6b3a42afSjmmv const int oldout = ::dup(STDOUT_FILENO);
142*6b3a42afSjmmv ATF_REQUIRE(oldout != -1);
143*6b3a42afSjmmv const int olderr = ::dup(STDERR_FILENO);
144*6b3a42afSjmmv ATF_REQUIRE(olderr != -1);
145*6b3a42afSjmmv
146*6b3a42afSjmmv const int fd = ::open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
147*6b3a42afSjmmv ATF_REQUIRE(fd != -1);
148*6b3a42afSjmmv ATF_REQUIRE(::dup2(fd, STDOUT_FILENO) != -1);
149*6b3a42afSjmmv ATF_REQUIRE(::dup2(fd, STDERR_FILENO) != -1);
150*6b3a42afSjmmv ::close(fd);
151*6b3a42afSjmmv
152*6b3a42afSjmmv return std::make_pair(oldout, olderr);
153*6b3a42afSjmmv }
154*6b3a42afSjmmv
155*6b3a42afSjmmv
156*6b3a42afSjmmv /// Restores stdout and stderr after a call to mock_stdfds.
157*6b3a42afSjmmv ///
158*6b3a42afSjmmv /// \param oldfds The copy of the previous stdout and stderr as returned by the
159*6b3a42afSjmmv /// call to mock_fds().
160*6b3a42afSjmmv static void
restore_stdfds(const std::pair<int,int> & oldfds)161*6b3a42afSjmmv restore_stdfds(const std::pair< int, int >& oldfds)
162*6b3a42afSjmmv {
163*6b3a42afSjmmv ATF_REQUIRE(::dup2(oldfds.first, STDOUT_FILENO) != -1);
164*6b3a42afSjmmv ::close(oldfds.first);
165*6b3a42afSjmmv ATF_REQUIRE(::dup2(oldfds.second, STDERR_FILENO) != -1);
166*6b3a42afSjmmv ::close(oldfds.second);
167*6b3a42afSjmmv }
168*6b3a42afSjmmv
169*6b3a42afSjmmv
170*6b3a42afSjmmv /// Checks whether a '+:' prefix to the short options of getopt_long works.
171*6b3a42afSjmmv ///
172*6b3a42afSjmmv /// It turns out that the getopt_long(3) implementation of Ubuntu 10.04.1 (and
173*6b3a42afSjmmv /// very likely other distributions) does not properly report a missing argument
174*6b3a42afSjmmv /// to a second long option as such. Instead of returning ':' when the second
175*6b3a42afSjmmv /// long option provided on the command line does not carry a required argument,
176*6b3a42afSjmmv /// it will mistakenly return '?' which translates to "unknown option".
177*6b3a42afSjmmv ///
178*6b3a42afSjmmv /// As a result of this bug, we cannot properly detect that 'flag2' requires an
179*6b3a42afSjmmv /// argument in a command line like: 'progname --flag1=foo --flag2'.
180*6b3a42afSjmmv ///
181*6b3a42afSjmmv /// I am not sure if we could fully workaround the issue in the implementation
182*6b3a42afSjmmv /// of our library. For the time being I am just using this bug detection in
183*6b3a42afSjmmv /// the test cases to prevent failures that are not really our fault.
184*6b3a42afSjmmv ///
185*6b3a42afSjmmv /// \return bool True if getopt_long is broken and does not interpret '+:'
186*6b3a42afSjmmv /// correctly; False otherwise.
187*6b3a42afSjmmv static bool
is_getopt_long_pluscolon_broken(void)188*6b3a42afSjmmv is_getopt_long_pluscolon_broken(void)
189*6b3a42afSjmmv {
190*6b3a42afSjmmv struct ::option long_options[] = {
191*6b3a42afSjmmv { "flag1", 1, NULL, '1' },
192*6b3a42afSjmmv { "flag2", 1, NULL, '2' },
193*6b3a42afSjmmv { NULL, 0, NULL, 0 }
194*6b3a42afSjmmv };
195*6b3a42afSjmmv
196*6b3a42afSjmmv const int argc = 3;
197*6b3a42afSjmmv char* argv[4];
198*6b3a42afSjmmv argv[0] = ::strdup("progname");
199*6b3a42afSjmmv argv[1] = ::strdup("--flag1=a");
200*6b3a42afSjmmv argv[2] = ::strdup("--flag2");
201*6b3a42afSjmmv argv[3] = NULL;
202*6b3a42afSjmmv
203*6b3a42afSjmmv const int old_opterr = ::opterr;
204*6b3a42afSjmmv ::opterr = 0;
205*6b3a42afSjmmv
206*6b3a42afSjmmv bool got_colon = false;
207*6b3a42afSjmmv
208*6b3a42afSjmmv int opt;
209*6b3a42afSjmmv while ((opt = ::getopt_long(argc, argv, "+:", long_options, NULL)) != -1) {
210*6b3a42afSjmmv switch (opt) {
211*6b3a42afSjmmv case '1': break;
212*6b3a42afSjmmv case '2': break;
213*6b3a42afSjmmv case ':': got_colon = true; break;
214*6b3a42afSjmmv case '?': break;
215*6b3a42afSjmmv default: UNREACHABLE; break;
216*6b3a42afSjmmv }
217*6b3a42afSjmmv }
218*6b3a42afSjmmv
219*6b3a42afSjmmv ::opterr = old_opterr;
220*6b3a42afSjmmv ::optind = 1;
221*6b3a42afSjmmv #if defined(HAVE_GETOPT_WITH_OPTRESET)
222*6b3a42afSjmmv ::optreset = 1;
223*6b3a42afSjmmv #endif
224*6b3a42afSjmmv
225*6b3a42afSjmmv for (char** arg = &argv[0]; *arg != NULL; arg++)
226*6b3a42afSjmmv std::free(*arg);
227*6b3a42afSjmmv
228*6b3a42afSjmmv return !got_colon;
229*6b3a42afSjmmv }
230*6b3a42afSjmmv
231*6b3a42afSjmmv
232*6b3a42afSjmmv } // anonymous namespace
233*6b3a42afSjmmv
234*6b3a42afSjmmv
235*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(progname__no_options);
ATF_TEST_CASE_BODY(progname__no_options)236*6b3a42afSjmmv ATF_TEST_CASE_BODY(progname__no_options)
237*6b3a42afSjmmv {
238*6b3a42afSjmmv const int argc = 1;
239*6b3a42afSjmmv const char* const argv[] = {"progname", NULL};
240*6b3a42afSjmmv std::vector< const base_option* > options;
241*6b3a42afSjmmv const parsed_cmdline cmdline = parse(argc, argv, options);
242*6b3a42afSjmmv
243*6b3a42afSjmmv ATF_REQUIRE(cmdline.arguments().empty());
244*6b3a42afSjmmv }
245*6b3a42afSjmmv
246*6b3a42afSjmmv
247*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(progname__some_options);
ATF_TEST_CASE_BODY(progname__some_options)248*6b3a42afSjmmv ATF_TEST_CASE_BODY(progname__some_options)
249*6b3a42afSjmmv {
250*6b3a42afSjmmv const int argc = 1;
251*6b3a42afSjmmv const char* const argv[] = {"progname", NULL};
252*6b3a42afSjmmv const string_option a('a', "a_option", "Foo", NULL);
253*6b3a42afSjmmv const string_option b('b', "b_option", "Bar", "arg", "foo");
254*6b3a42afSjmmv const string_option c("c_option", "Baz", NULL);
255*6b3a42afSjmmv const string_option d("d_option", "Wohoo", "arg", "bar");
256*6b3a42afSjmmv std::vector< const base_option* > options;
257*6b3a42afSjmmv options.push_back(&a);
258*6b3a42afSjmmv options.push_back(&b);
259*6b3a42afSjmmv options.push_back(&c);
260*6b3a42afSjmmv options.push_back(&d);
261*6b3a42afSjmmv const parsed_cmdline cmdline = parse(argc, argv, options);
262*6b3a42afSjmmv
263*6b3a42afSjmmv ATF_REQUIRE_EQ("foo", cmdline.get_option< string_option >("b_option"));
264*6b3a42afSjmmv ATF_REQUIRE_EQ("bar", cmdline.get_option< string_option >("d_option"));
265*6b3a42afSjmmv ATF_REQUIRE(cmdline.arguments().empty());
266*6b3a42afSjmmv }
267*6b3a42afSjmmv
268*6b3a42afSjmmv
269*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(some_args__no_options);
ATF_TEST_CASE_BODY(some_args__no_options)270*6b3a42afSjmmv ATF_TEST_CASE_BODY(some_args__no_options)
271*6b3a42afSjmmv {
272*6b3a42afSjmmv const int argc = 5;
273*6b3a42afSjmmv const char* const argv[] = {"progname", "foo", "-c", "--opt", "bar", NULL};
274*6b3a42afSjmmv std::vector< const base_option* > options;
275*6b3a42afSjmmv const parsed_cmdline cmdline = parse(argc, argv, options);
276*6b3a42afSjmmv
277*6b3a42afSjmmv ATF_REQUIRE(!cmdline.has_option("c"));
278*6b3a42afSjmmv ATF_REQUIRE(!cmdline.has_option("opt"));
279*6b3a42afSjmmv ATF_REQUIRE_EQ(4, cmdline.arguments().size());
280*6b3a42afSjmmv ATF_REQUIRE_EQ("foo", cmdline.arguments()[0]);
281*6b3a42afSjmmv ATF_REQUIRE_EQ("-c", cmdline.arguments()[1]);
282*6b3a42afSjmmv ATF_REQUIRE_EQ("--opt", cmdline.arguments()[2]);
283*6b3a42afSjmmv ATF_REQUIRE_EQ("bar", cmdline.arguments()[3]);
284*6b3a42afSjmmv }
285*6b3a42afSjmmv
286*6b3a42afSjmmv
287*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(some_args__some_options);
ATF_TEST_CASE_BODY(some_args__some_options)288*6b3a42afSjmmv ATF_TEST_CASE_BODY(some_args__some_options)
289*6b3a42afSjmmv {
290*6b3a42afSjmmv const int argc = 5;
291*6b3a42afSjmmv const char* const argv[] = {"progname", "foo", "-c", "--opt", "bar", NULL};
292*6b3a42afSjmmv const string_option c('c', "opt", "Description", NULL);
293*6b3a42afSjmmv std::vector< const base_option* > options;
294*6b3a42afSjmmv options.push_back(&c);
295*6b3a42afSjmmv const parsed_cmdline cmdline = parse(argc, argv, options);
296*6b3a42afSjmmv
297*6b3a42afSjmmv ATF_REQUIRE(!cmdline.has_option("c"));
298*6b3a42afSjmmv ATF_REQUIRE(!cmdline.has_option("opt"));
299*6b3a42afSjmmv ATF_REQUIRE_EQ(4, cmdline.arguments().size());
300*6b3a42afSjmmv ATF_REQUIRE_EQ("foo", cmdline.arguments()[0]);
301*6b3a42afSjmmv ATF_REQUIRE_EQ("-c", cmdline.arguments()[1]);
302*6b3a42afSjmmv ATF_REQUIRE_EQ("--opt", cmdline.arguments()[2]);
303*6b3a42afSjmmv ATF_REQUIRE_EQ("bar", cmdline.arguments()[3]);
304*6b3a42afSjmmv }
305*6b3a42afSjmmv
306*6b3a42afSjmmv
307*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(some_options__all_known);
ATF_TEST_CASE_BODY(some_options__all_known)308*6b3a42afSjmmv ATF_TEST_CASE_BODY(some_options__all_known)
309*6b3a42afSjmmv {
310*6b3a42afSjmmv const int argc = 14;
311*6b3a42afSjmmv const char* const argv[] = {
312*6b3a42afSjmmv "progname",
313*6b3a42afSjmmv "-a",
314*6b3a42afSjmmv "-bvalue_b",
315*6b3a42afSjmmv "-c", "value_c",
316*6b3a42afSjmmv //"-d", // Options with default optional values are unsupported.
317*6b3a42afSjmmv "-evalue_e", // Has default; overriden.
318*6b3a42afSjmmv "--f_long",
319*6b3a42afSjmmv "--g_long=value_g",
320*6b3a42afSjmmv "--h_long", "value_h",
321*6b3a42afSjmmv //"--i_long", // Options with default optional values are unsupported.
322*6b3a42afSjmmv "--j_long", "value_j", // Has default; overriden as separate argument.
323*6b3a42afSjmmv "arg1", "arg2", NULL,
324*6b3a42afSjmmv };
325*6b3a42afSjmmv const bool_option a('a', "a_long", "");
326*6b3a42afSjmmv const string_option b('b', "b_long", "Description", "arg");
327*6b3a42afSjmmv const string_option c('c', "c_long", "ABCD", "foo");
328*6b3a42afSjmmv const string_option d('d', "d_long", "Description", "bar", "default_d");
329*6b3a42afSjmmv const string_option e('e', "e_long", "Description", "baz", "default_e");
330*6b3a42afSjmmv const bool_option f("f_long", "Description");
331*6b3a42afSjmmv const string_option g("g_long", "Description", "arg");
332*6b3a42afSjmmv const string_option h("h_long", "Description", "foo");
333*6b3a42afSjmmv const string_option i("i_long", "EFGH", "bar", "default_i");
334*6b3a42afSjmmv const string_option j("j_long", "Description", "baz", "default_j");
335*6b3a42afSjmmv std::vector< const base_option* > options;
336*6b3a42afSjmmv options.push_back(&a);
337*6b3a42afSjmmv options.push_back(&b);
338*6b3a42afSjmmv options.push_back(&c);
339*6b3a42afSjmmv options.push_back(&d);
340*6b3a42afSjmmv options.push_back(&e);
341*6b3a42afSjmmv options.push_back(&f);
342*6b3a42afSjmmv options.push_back(&g);
343*6b3a42afSjmmv options.push_back(&h);
344*6b3a42afSjmmv options.push_back(&i);
345*6b3a42afSjmmv options.push_back(&j);
346*6b3a42afSjmmv const parsed_cmdline cmdline = parse(argc, argv, options);
347*6b3a42afSjmmv
348*6b3a42afSjmmv ATF_REQUIRE(cmdline.has_option("a_long"));
349*6b3a42afSjmmv ATF_REQUIRE_EQ("value_b", cmdline.get_option< string_option >("b_long"));
350*6b3a42afSjmmv ATF_REQUIRE_EQ("value_c", cmdline.get_option< string_option >("c_long"));
351*6b3a42afSjmmv ATF_REQUIRE_EQ("default_d", cmdline.get_option< string_option >("d_long"));
352*6b3a42afSjmmv ATF_REQUIRE_EQ("value_e", cmdline.get_option< string_option >("e_long"));
353*6b3a42afSjmmv ATF_REQUIRE(cmdline.has_option("f_long"));
354*6b3a42afSjmmv ATF_REQUIRE_EQ("value_g", cmdline.get_option< string_option >("g_long"));
355*6b3a42afSjmmv ATF_REQUIRE_EQ("value_h", cmdline.get_option< string_option >("h_long"));
356*6b3a42afSjmmv ATF_REQUIRE_EQ("default_i", cmdline.get_option< string_option >("i_long"));
357*6b3a42afSjmmv ATF_REQUIRE_EQ("value_j", cmdline.get_option< string_option >("j_long"));
358*6b3a42afSjmmv ATF_REQUIRE_EQ(2, cmdline.arguments().size());
359*6b3a42afSjmmv ATF_REQUIRE_EQ("arg1", cmdline.arguments()[0]);
360*6b3a42afSjmmv ATF_REQUIRE_EQ("arg2", cmdline.arguments()[1]);
361*6b3a42afSjmmv }
362*6b3a42afSjmmv
363*6b3a42afSjmmv
364*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(some_options__multi);
ATF_TEST_CASE_BODY(some_options__multi)365*6b3a42afSjmmv ATF_TEST_CASE_BODY(some_options__multi)
366*6b3a42afSjmmv {
367*6b3a42afSjmmv const int argc = 9;
368*6b3a42afSjmmv const char* const argv[] = {
369*6b3a42afSjmmv "progname",
370*6b3a42afSjmmv "-a1",
371*6b3a42afSjmmv "-bvalue1",
372*6b3a42afSjmmv "-a2",
373*6b3a42afSjmmv "--a_long=3",
374*6b3a42afSjmmv "-bvalue2",
375*6b3a42afSjmmv "--b_long=value3",
376*6b3a42afSjmmv "arg1", "arg2", NULL,
377*6b3a42afSjmmv };
378*6b3a42afSjmmv const int_option a('a', "a_long", "Description", "arg");
379*6b3a42afSjmmv const string_option b('b', "b_long", "Description", "arg");
380*6b3a42afSjmmv std::vector< const base_option* > options;
381*6b3a42afSjmmv options.push_back(&a);
382*6b3a42afSjmmv options.push_back(&b);
383*6b3a42afSjmmv const parsed_cmdline cmdline = parse(argc, argv, options);
384*6b3a42afSjmmv
385*6b3a42afSjmmv {
386*6b3a42afSjmmv ATF_REQUIRE_EQ(3, cmdline.get_option< int_option >("a_long"));
387*6b3a42afSjmmv const std::vector< int > multi =
388*6b3a42afSjmmv cmdline.get_multi_option< int_option >("a_long");
389*6b3a42afSjmmv ATF_REQUIRE_EQ(3, multi.size());
390*6b3a42afSjmmv ATF_REQUIRE_EQ(1, multi[0]);
391*6b3a42afSjmmv ATF_REQUIRE_EQ(2, multi[1]);
392*6b3a42afSjmmv ATF_REQUIRE_EQ(3, multi[2]);
393*6b3a42afSjmmv }
394*6b3a42afSjmmv
395*6b3a42afSjmmv {
396*6b3a42afSjmmv ATF_REQUIRE_EQ("value3", cmdline.get_option< string_option >("b_long"));
397*6b3a42afSjmmv const std::vector< std::string > multi =
398*6b3a42afSjmmv cmdline.get_multi_option< string_option >("b_long");
399*6b3a42afSjmmv ATF_REQUIRE_EQ(3, multi.size());
400*6b3a42afSjmmv ATF_REQUIRE_EQ("value1", multi[0]);
401*6b3a42afSjmmv ATF_REQUIRE_EQ("value2", multi[1]);
402*6b3a42afSjmmv ATF_REQUIRE_EQ("value3", multi[2]);
403*6b3a42afSjmmv }
404*6b3a42afSjmmv }
405*6b3a42afSjmmv
406*6b3a42afSjmmv
407*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(subcommands);
ATF_TEST_CASE_BODY(subcommands)408*6b3a42afSjmmv ATF_TEST_CASE_BODY(subcommands)
409*6b3a42afSjmmv {
410*6b3a42afSjmmv const int argc = 5;
411*6b3a42afSjmmv const char* const argv[] = {"progname", "--flag1", "subcommand",
412*6b3a42afSjmmv "--flag2", "arg", NULL};
413*6b3a42afSjmmv const bool_option flag1("flag1", "");
414*6b3a42afSjmmv std::vector< const base_option* > options;
415*6b3a42afSjmmv options.push_back(&flag1);
416*6b3a42afSjmmv const parsed_cmdline cmdline = parse(argc, argv, options);
417*6b3a42afSjmmv
418*6b3a42afSjmmv ATF_REQUIRE( cmdline.has_option("flag1"));
419*6b3a42afSjmmv ATF_REQUIRE(!cmdline.has_option("flag2"));
420*6b3a42afSjmmv ATF_REQUIRE_EQ(3, cmdline.arguments().size());
421*6b3a42afSjmmv ATF_REQUIRE_EQ("subcommand", cmdline.arguments()[0]);
422*6b3a42afSjmmv ATF_REQUIRE_EQ("--flag2", cmdline.arguments()[1]);
423*6b3a42afSjmmv ATF_REQUIRE_EQ("arg", cmdline.arguments()[2]);
424*6b3a42afSjmmv
425*6b3a42afSjmmv const bool_option flag2("flag2", "");
426*6b3a42afSjmmv std::vector< const base_option* > options2;
427*6b3a42afSjmmv options2.push_back(&flag2);
428*6b3a42afSjmmv const parsed_cmdline cmdline2 = parse(cmdline.arguments(), options2);
429*6b3a42afSjmmv
430*6b3a42afSjmmv ATF_REQUIRE(!cmdline2.has_option("flag1"));
431*6b3a42afSjmmv ATF_REQUIRE( cmdline2.has_option("flag2"));
432*6b3a42afSjmmv ATF_REQUIRE_EQ(1, cmdline2.arguments().size());
433*6b3a42afSjmmv ATF_REQUIRE_EQ("arg", cmdline2.arguments()[0]);
434*6b3a42afSjmmv }
435*6b3a42afSjmmv
436*6b3a42afSjmmv
437*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__short);
ATF_TEST_CASE_BODY(missing_option_argument_error__short)438*6b3a42afSjmmv ATF_TEST_CASE_BODY(missing_option_argument_error__short)
439*6b3a42afSjmmv {
440*6b3a42afSjmmv const int argc = 3;
441*6b3a42afSjmmv const char* const argv[] = {"progname", "-a3", "-b", NULL};
442*6b3a42afSjmmv const string_option flag1('a', "flag1", "Description", "arg");
443*6b3a42afSjmmv const string_option flag2('b', "flag2", "Description", "arg");
444*6b3a42afSjmmv std::vector< const base_option* > options;
445*6b3a42afSjmmv options.push_back(&flag1);
446*6b3a42afSjmmv options.push_back(&flag2);
447*6b3a42afSjmmv
448*6b3a42afSjmmv try {
449*6b3a42afSjmmv parse(argc, argv, options);
450*6b3a42afSjmmv fail("missing_option_argument_error not raised");
451*6b3a42afSjmmv } catch (const cmdline::missing_option_argument_error& e) {
452*6b3a42afSjmmv ATF_REQUIRE_EQ("-b", e.option());
453*6b3a42afSjmmv } catch (const cmdline::unknown_option_error& e) {
454*6b3a42afSjmmv if (is_getopt_long_pluscolon_broken())
455*6b3a42afSjmmv expect_fail("Your getopt_long is broken");
456*6b3a42afSjmmv fail("Got unknown_option_error instead of "
457*6b3a42afSjmmv "missing_option_argument_error");
458*6b3a42afSjmmv }
459*6b3a42afSjmmv }
460*6b3a42afSjmmv
461*6b3a42afSjmmv
462*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__shortblock);
ATF_TEST_CASE_BODY(missing_option_argument_error__shortblock)463*6b3a42afSjmmv ATF_TEST_CASE_BODY(missing_option_argument_error__shortblock)
464*6b3a42afSjmmv {
465*6b3a42afSjmmv const int argc = 3;
466*6b3a42afSjmmv const char* const argv[] = {"progname", "-ab3", "-ac", NULL};
467*6b3a42afSjmmv const bool_option flag1('a', "flag1", "Description");
468*6b3a42afSjmmv const string_option flag2('b', "flag2", "Description", "arg");
469*6b3a42afSjmmv const string_option flag3('c', "flag2", "Description", "arg");
470*6b3a42afSjmmv std::vector< const base_option* > options;
471*6b3a42afSjmmv options.push_back(&flag1);
472*6b3a42afSjmmv options.push_back(&flag2);
473*6b3a42afSjmmv options.push_back(&flag3);
474*6b3a42afSjmmv
475*6b3a42afSjmmv try {
476*6b3a42afSjmmv parse(argc, argv, options);
477*6b3a42afSjmmv fail("missing_option_argument_error not raised");
478*6b3a42afSjmmv } catch (const cmdline::missing_option_argument_error& e) {
479*6b3a42afSjmmv ATF_REQUIRE_EQ("-c", e.option());
480*6b3a42afSjmmv } catch (const cmdline::unknown_option_error& e) {
481*6b3a42afSjmmv if (is_getopt_long_pluscolon_broken())
482*6b3a42afSjmmv expect_fail("Your getopt_long is broken");
483*6b3a42afSjmmv fail("Got unknown_option_error instead of "
484*6b3a42afSjmmv "missing_option_argument_error");
485*6b3a42afSjmmv }
486*6b3a42afSjmmv }
487*6b3a42afSjmmv
488*6b3a42afSjmmv
489*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__long);
ATF_TEST_CASE_BODY(missing_option_argument_error__long)490*6b3a42afSjmmv ATF_TEST_CASE_BODY(missing_option_argument_error__long)
491*6b3a42afSjmmv {
492*6b3a42afSjmmv const int argc = 3;
493*6b3a42afSjmmv const char* const argv[] = {"progname", "--flag1=a", "--flag2", NULL};
494*6b3a42afSjmmv const string_option flag1("flag1", "Description", "arg");
495*6b3a42afSjmmv const string_option flag2("flag2", "Description", "arg");
496*6b3a42afSjmmv std::vector< const base_option* > options;
497*6b3a42afSjmmv options.push_back(&flag1);
498*6b3a42afSjmmv options.push_back(&flag2);
499*6b3a42afSjmmv
500*6b3a42afSjmmv try {
501*6b3a42afSjmmv parse(argc, argv, options);
502*6b3a42afSjmmv fail("missing_option_argument_error not raised");
503*6b3a42afSjmmv } catch (const cmdline::missing_option_argument_error& e) {
504*6b3a42afSjmmv ATF_REQUIRE_EQ("--flag2", e.option());
505*6b3a42afSjmmv } catch (const cmdline::unknown_option_error& e) {
506*6b3a42afSjmmv if (is_getopt_long_pluscolon_broken())
507*6b3a42afSjmmv expect_fail("Your getopt_long is broken");
508*6b3a42afSjmmv fail("Got unknown_option_error instead of "
509*6b3a42afSjmmv "missing_option_argument_error");
510*6b3a42afSjmmv }
511*6b3a42afSjmmv }
512*6b3a42afSjmmv
513*6b3a42afSjmmv
514*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__short);
ATF_TEST_CASE_BODY(unknown_option_error__short)515*6b3a42afSjmmv ATF_TEST_CASE_BODY(unknown_option_error__short)
516*6b3a42afSjmmv {
517*6b3a42afSjmmv const int argc = 3;
518*6b3a42afSjmmv const char* const argv[] = {"progname", "-a", "-b", NULL};
519*6b3a42afSjmmv const bool_option flag1('a', "flag1", "Description");
520*6b3a42afSjmmv std::vector< const base_option* > options;
521*6b3a42afSjmmv options.push_back(&flag1);
522*6b3a42afSjmmv
523*6b3a42afSjmmv try {
524*6b3a42afSjmmv parse(argc, argv, options);
525*6b3a42afSjmmv fail("unknown_option_error not raised");
526*6b3a42afSjmmv } catch (const cmdline::unknown_option_error& e) {
527*6b3a42afSjmmv ATF_REQUIRE_EQ("-b", e.option());
528*6b3a42afSjmmv }
529*6b3a42afSjmmv }
530*6b3a42afSjmmv
531*6b3a42afSjmmv
532*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__shortblock);
ATF_TEST_CASE_BODY(unknown_option_error__shortblock)533*6b3a42afSjmmv ATF_TEST_CASE_BODY(unknown_option_error__shortblock)
534*6b3a42afSjmmv {
535*6b3a42afSjmmv const int argc = 3;
536*6b3a42afSjmmv const char* const argv[] = {"progname", "-a", "-bdc", NULL};
537*6b3a42afSjmmv const bool_option flag1('a', "flag1", "Description");
538*6b3a42afSjmmv const bool_option flag2('b', "flag2", "Description");
539*6b3a42afSjmmv const bool_option flag3('c', "flag3", "Description");
540*6b3a42afSjmmv std::vector< const base_option* > options;
541*6b3a42afSjmmv options.push_back(&flag1);
542*6b3a42afSjmmv options.push_back(&flag2);
543*6b3a42afSjmmv options.push_back(&flag3);
544*6b3a42afSjmmv
545*6b3a42afSjmmv try {
546*6b3a42afSjmmv parse(argc, argv, options);
547*6b3a42afSjmmv fail("unknown_option_error not raised");
548*6b3a42afSjmmv } catch (const cmdline::unknown_option_error& e) {
549*6b3a42afSjmmv ATF_REQUIRE_EQ("-d", e.option());
550*6b3a42afSjmmv }
551*6b3a42afSjmmv }
552*6b3a42afSjmmv
553*6b3a42afSjmmv
554*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__long);
ATF_TEST_CASE_BODY(unknown_option_error__long)555*6b3a42afSjmmv ATF_TEST_CASE_BODY(unknown_option_error__long)
556*6b3a42afSjmmv {
557*6b3a42afSjmmv const int argc = 3;
558*6b3a42afSjmmv const char* const argv[] = {"progname", "--flag1=a", "--flag2", NULL};
559*6b3a42afSjmmv const string_option flag1("flag1", "Description", "arg");
560*6b3a42afSjmmv std::vector< const base_option* > options;
561*6b3a42afSjmmv options.push_back(&flag1);
562*6b3a42afSjmmv
563*6b3a42afSjmmv try {
564*6b3a42afSjmmv parse(argc, argv, options);
565*6b3a42afSjmmv fail("unknown_option_error not raised");
566*6b3a42afSjmmv } catch (const cmdline::unknown_option_error& e) {
567*6b3a42afSjmmv ATF_REQUIRE_EQ("--flag2", e.option());
568*6b3a42afSjmmv }
569*6b3a42afSjmmv }
570*6b3a42afSjmmv
571*6b3a42afSjmmv
572*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(unknown_plus_option_error);
ATF_TEST_CASE_BODY(unknown_plus_option_error)573*6b3a42afSjmmv ATF_TEST_CASE_BODY(unknown_plus_option_error)
574*6b3a42afSjmmv {
575*6b3a42afSjmmv const int argc = 2;
576*6b3a42afSjmmv const char* const argv[] = {"progname", "-+", NULL};
577*6b3a42afSjmmv const cmdline::options_vector options;
578*6b3a42afSjmmv
579*6b3a42afSjmmv try {
580*6b3a42afSjmmv parse(argc, argv, options);
581*6b3a42afSjmmv fail("unknown_option_error not raised");
582*6b3a42afSjmmv } catch (const cmdline::unknown_option_error& e) {
583*6b3a42afSjmmv ATF_REQUIRE_EQ("-+", e.option());
584*6b3a42afSjmmv } catch (const cmdline::missing_option_argument_error& e) {
585*6b3a42afSjmmv fail("Looks like getopt_long thinks a + option is defined and it "
586*6b3a42afSjmmv "even requires an argument");
587*6b3a42afSjmmv }
588*6b3a42afSjmmv }
589*6b3a42afSjmmv
590*6b3a42afSjmmv
591*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(option_types);
ATF_TEST_CASE_BODY(option_types)592*6b3a42afSjmmv ATF_TEST_CASE_BODY(option_types)
593*6b3a42afSjmmv {
594*6b3a42afSjmmv const int argc = 3;
595*6b3a42afSjmmv const char* const argv[] = {"progname", "--flag1=a", "--flag2=one", NULL};
596*6b3a42afSjmmv const string_option flag1("flag1", "The flag1", "arg");
597*6b3a42afSjmmv const mock_option flag2("flag2");
598*6b3a42afSjmmv std::vector< const base_option* > options;
599*6b3a42afSjmmv options.push_back(&flag1);
600*6b3a42afSjmmv options.push_back(&flag2);
601*6b3a42afSjmmv
602*6b3a42afSjmmv const parsed_cmdline cmdline = parse(argc, argv, options);
603*6b3a42afSjmmv
604*6b3a42afSjmmv ATF_REQUIRE(cmdline.has_option("flag1"));
605*6b3a42afSjmmv ATF_REQUIRE(cmdline.has_option("flag2"));
606*6b3a42afSjmmv ATF_REQUIRE_EQ("a", cmdline.get_option< string_option >("flag1"));
607*6b3a42afSjmmv ATF_REQUIRE_EQ(1, cmdline.get_option< mock_option >("flag2"));
608*6b3a42afSjmmv }
609*6b3a42afSjmmv
610*6b3a42afSjmmv
611*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(option_validation_error);
ATF_TEST_CASE_BODY(option_validation_error)612*6b3a42afSjmmv ATF_TEST_CASE_BODY(option_validation_error)
613*6b3a42afSjmmv {
614*6b3a42afSjmmv const int argc = 3;
615*6b3a42afSjmmv const char* const argv[] = {"progname", "--flag1=zero", "--flag2=foo",
616*6b3a42afSjmmv NULL};
617*6b3a42afSjmmv const mock_option flag1("flag1");
618*6b3a42afSjmmv const mock_option flag2("flag2");
619*6b3a42afSjmmv std::vector< const base_option* > options;
620*6b3a42afSjmmv options.push_back(&flag1);
621*6b3a42afSjmmv options.push_back(&flag2);
622*6b3a42afSjmmv
623*6b3a42afSjmmv try {
624*6b3a42afSjmmv parse(argc, argv, options);
625*6b3a42afSjmmv fail("option_argument_value_error not raised");
626*6b3a42afSjmmv } catch (const cmdline::option_argument_value_error& e) {
627*6b3a42afSjmmv ATF_REQUIRE_EQ("--flag2", e.option());
628*6b3a42afSjmmv ATF_REQUIRE_EQ("foo", e.argument());
629*6b3a42afSjmmv }
630*6b3a42afSjmmv }
631*6b3a42afSjmmv
632*6b3a42afSjmmv
633*6b3a42afSjmmv ATF_TEST_CASE_WITHOUT_HEAD(silent_errors);
ATF_TEST_CASE_BODY(silent_errors)634*6b3a42afSjmmv ATF_TEST_CASE_BODY(silent_errors)
635*6b3a42afSjmmv {
636*6b3a42afSjmmv const int argc = 2;
637*6b3a42afSjmmv const char* const argv[] = {"progname", "-h", NULL};
638*6b3a42afSjmmv cmdline::options_vector options;
639*6b3a42afSjmmv
640*6b3a42afSjmmv try {
641*6b3a42afSjmmv std::pair< int, int > oldfds = mock_stdfds("output.txt");
642*6b3a42afSjmmv try {
643*6b3a42afSjmmv parse(argc, argv, options);
644*6b3a42afSjmmv } catch (...) {
645*6b3a42afSjmmv restore_stdfds(oldfds);
646*6b3a42afSjmmv throw;
647*6b3a42afSjmmv }
648*6b3a42afSjmmv restore_stdfds(oldfds);
649*6b3a42afSjmmv fail("unknown_option_error not raised");
650*6b3a42afSjmmv } catch (const cmdline::unknown_option_error& e) {
651*6b3a42afSjmmv ATF_REQUIRE_EQ("-h", e.option());
652*6b3a42afSjmmv }
653*6b3a42afSjmmv
654*6b3a42afSjmmv std::ifstream input("output.txt");
655*6b3a42afSjmmv ATF_REQUIRE(input);
656*6b3a42afSjmmv
657*6b3a42afSjmmv bool has_output = false;
658*6b3a42afSjmmv std::string line;
659*6b3a42afSjmmv while (std::getline(input, line).good()) {
660*6b3a42afSjmmv std::cout << line << '\n';
661*6b3a42afSjmmv has_output = true;
662*6b3a42afSjmmv }
663*6b3a42afSjmmv
664*6b3a42afSjmmv if (has_output)
665*6b3a42afSjmmv fail("getopt_long printed messages on stdout/stderr by itself");
666*6b3a42afSjmmv }
667*6b3a42afSjmmv
668*6b3a42afSjmmv
ATF_INIT_TEST_CASES(tcs)669*6b3a42afSjmmv ATF_INIT_TEST_CASES(tcs)
670*6b3a42afSjmmv {
671*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, progname__no_options);
672*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, progname__some_options);
673*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, some_args__no_options);
674*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, some_args__some_options);
675*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, some_options__all_known);
676*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, some_options__multi);
677*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, subcommands);
678*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__short);
679*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__shortblock);
680*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__long);
681*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, unknown_option_error__short);
682*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, unknown_option_error__shortblock);
683*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, unknown_option_error__long);
684*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, unknown_plus_option_error);
685*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, option_types);
686*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, option_validation_error);
687*6b3a42afSjmmv ATF_ADD_TEST_CASE(tcs, silent_errors);
688*6b3a42afSjmmv }
689