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/base_command.ipp"
30
31#include <atf-c++.hpp>
32
33#include "utils/cmdline/exceptions.hpp"
34#include "utils/cmdline/options.hpp"
35#include "utils/cmdline/parser.ipp"
36#include "utils/cmdline/ui_mock.hpp"
37#include "utils/defs.hpp"
38
39namespace cmdline = utils::cmdline;
40
41
42namespace {
43
44
45/// Mock command to test the cmdline::base_command base class.
46///
47/// \param Data The type of the opaque data object passed to main().
48/// \param ExpectedData The value run() will expect to find in the Data object
49///     passed to main().
50template< typename Data, Data ExpectedData >
51class mock_cmd : public cmdline::base_command< Data > {
52public:
53    /// Indicates if run() has been called already and executed correctly.
54    bool executed;
55
56    /// Contains the argument of --the_string after run() is executed.
57    std::string optvalue;
58
59    /// Constructs a new mock command.
60    mock_cmd(void) :
61        cmdline::base_command< Data >("mock", "arg1 [arg2 [arg3]]", 1, 3,
62                                      "Command for testing."),
63        executed(false)
64    {
65        this->add_option(cmdline::string_option("the_string", "Test option",
66                                                "arg"));
67    }
68
69    /// Executes the command.
70    ///
71    /// \param cmdline Representation of the command line to the subcommand.
72    /// \param data Arbitrary data cookie passed to the command.
73    ///
74    /// \return A hardcoded number for testing purposes.
75    int
76    run(cmdline::ui* /* ui */,
77        const cmdline::parsed_cmdline& cmdline, const Data& data)
78    {
79        if (cmdline.has_option("the_string"))
80            optvalue = cmdline.get_option< cmdline::string_option >(
81                "the_string");
82        ATF_REQUIRE_EQ(ExpectedData, data);
83        executed = true;
84        return 1234;
85    }
86};
87
88
89/// Mock command to test the cmdline::base_command_no_data base class.
90class mock_cmd_no_data : public cmdline::base_command_no_data {
91public:
92    /// Indicates if run() has been called already and executed correctly.
93    bool executed;
94
95    /// Contains the argument of --the_string after run() is executed.
96    std::string optvalue;
97
98    /// Constructs a new mock command.
99    mock_cmd_no_data(void) :
100        cmdline::base_command_no_data("mock", "arg1 [arg2 [arg3]]", 1, 3,
101                                      "Command for testing."),
102        executed(false)
103    {
104        add_option(cmdline::string_option("the_string", "Test option", "arg"));
105    }
106
107    /// Executes the command.
108    ///
109    /// \param cmdline Representation of the command line to the subcommand.
110    ///
111    /// \return A hardcoded number for testing purposes.
112    int
113    run(cmdline::ui* /* ui */,
114        const cmdline::parsed_cmdline& cmdline)
115    {
116        if (cmdline.has_option("the_string"))
117            optvalue = cmdline.get_option< cmdline::string_option >(
118                "the_string");
119        executed = true;
120        return 1234;
121    }
122};
123
124
125/// Implementation of a command to get access to parse_cmdline().
126class parse_cmdline_portal : public cmdline::command_proto {
127public:
128    /// Constructs a new mock command.
129    parse_cmdline_portal(void) :
130        cmdline::command_proto("portal", "arg1 [arg2 [arg3]]", 1, 3,
131                               "Command for testing.")
132    {
133        this->add_option(cmdline::string_option("the_string", "Test option",
134                                                "arg"));
135    }
136
137    /// Delegator for the internal parse_cmdline() method.
138    ///
139    /// \param args The input arguments to be parsed.
140    ///
141    /// \return The parsed command line, split in options and arguments.
142    cmdline::parsed_cmdline
143    operator()(const cmdline::args_vector& args) const
144    {
145        return parse_cmdline(args);
146    }
147};
148
149
150}  // anonymous namespace
151
152
153ATF_TEST_CASE_WITHOUT_HEAD(command_proto__parse_cmdline__ok);
154ATF_TEST_CASE_BODY(command_proto__parse_cmdline__ok)
155{
156    cmdline::args_vector args;
157    args.push_back("portal");
158    args.push_back("--the_string=foo bar");
159    args.push_back("one arg");
160    args.push_back("another arg");
161    (void)parse_cmdline_portal()(args);
162}
163
164
165ATF_TEST_CASE_WITHOUT_HEAD(command_proto__parse_cmdline__parse_fail);
166ATF_TEST_CASE_BODY(command_proto__parse_cmdline__parse_fail)
167{
168    cmdline::args_vector args;
169    args.push_back("portal");
170    args.push_back("--foo-bar");
171    ATF_REQUIRE_THROW_RE(cmdline::usage_error, "Unknown.*foo-bar",
172                         (void)parse_cmdline_portal()(args));
173}
174
175
176ATF_TEST_CASE_WITHOUT_HEAD(command_proto__parse_cmdline__args_invalid);
177ATF_TEST_CASE_BODY(command_proto__parse_cmdline__args_invalid)
178{
179    cmdline::args_vector args;
180    args.push_back("portal");
181
182    ATF_REQUIRE_THROW_RE(cmdline::usage_error, "Not enough arguments",
183                         (void)parse_cmdline_portal()(args));
184
185    args.push_back("1");
186    args.push_back("2");
187    args.push_back("3");
188    args.push_back("4");
189    ATF_REQUIRE_THROW_RE(cmdline::usage_error, "Too many arguments",
190                         (void)parse_cmdline_portal()(args));
191}
192
193
194ATF_TEST_CASE_WITHOUT_HEAD(base_command__getters);
195ATF_TEST_CASE_BODY(base_command__getters)
196{
197    mock_cmd< int, 584 > cmd;
198    ATF_REQUIRE_EQ("mock", cmd.name());
199    ATF_REQUIRE_EQ("arg1 [arg2 [arg3]]", cmd.arg_list());
200    ATF_REQUIRE_EQ("Command for testing.", cmd.short_description());
201    ATF_REQUIRE_EQ(1, cmd.options().size());
202    ATF_REQUIRE_EQ("the_string", cmd.options()[0]->long_name());
203}
204
205
206ATF_TEST_CASE_WITHOUT_HEAD(base_command__main__ok)
207ATF_TEST_CASE_BODY(base_command__main__ok)
208{
209    mock_cmd< int, 584 > cmd;
210
211    cmdline::ui_mock ui;
212    cmdline::args_vector args;
213    args.push_back("mock");
214    args.push_back("--the_string=foo bar");
215    args.push_back("one arg");
216    args.push_back("another arg");
217    ATF_REQUIRE_EQ(1234, cmd.main(&ui, args, 584));
218    ATF_REQUIRE(cmd.executed);
219    ATF_REQUIRE_EQ("foo bar", cmd.optvalue);
220}
221
222
223ATF_TEST_CASE_WITHOUT_HEAD(base_command__main__parse_cmdline_fail)
224ATF_TEST_CASE_BODY(base_command__main__parse_cmdline_fail)
225{
226    mock_cmd< int, 584 > cmd;
227
228    cmdline::ui_mock ui;
229    cmdline::args_vector args;
230    args.push_back("mock");
231    args.push_back("--foo-bar");
232    ATF_REQUIRE_THROW_RE(cmdline::usage_error, "Unknown.*foo-bar",
233                         cmd.main(&ui, args, 584));
234    ATF_REQUIRE(!cmd.executed);
235}
236
237
238ATF_TEST_CASE_WITHOUT_HEAD(base_command_no_data__getters);
239ATF_TEST_CASE_BODY(base_command_no_data__getters)
240{
241    mock_cmd_no_data cmd;
242    ATF_REQUIRE_EQ("mock", cmd.name());
243    ATF_REQUIRE_EQ("arg1 [arg2 [arg3]]", cmd.arg_list());
244    ATF_REQUIRE_EQ("Command for testing.", cmd.short_description());
245    ATF_REQUIRE_EQ(1, cmd.options().size());
246    ATF_REQUIRE_EQ("the_string", cmd.options()[0]->long_name());
247}
248
249
250ATF_TEST_CASE_WITHOUT_HEAD(base_command_no_data__main__ok)
251ATF_TEST_CASE_BODY(base_command_no_data__main__ok)
252{
253    mock_cmd_no_data cmd;
254
255    cmdline::ui_mock ui;
256    cmdline::args_vector args;
257    args.push_back("mock");
258    args.push_back("--the_string=foo bar");
259    args.push_back("one arg");
260    args.push_back("another arg");
261    ATF_REQUIRE_EQ(1234, cmd.main(&ui, args));
262    ATF_REQUIRE(cmd.executed);
263    ATF_REQUIRE_EQ("foo bar", cmd.optvalue);
264}
265
266
267ATF_TEST_CASE_WITHOUT_HEAD(base_command_no_data__main__parse_cmdline_fail)
268ATF_TEST_CASE_BODY(base_command_no_data__main__parse_cmdline_fail)
269{
270    mock_cmd_no_data cmd;
271
272    cmdline::ui_mock ui;
273    cmdline::args_vector args;
274    args.push_back("mock");
275    args.push_back("--foo-bar");
276    ATF_REQUIRE_THROW_RE(cmdline::usage_error, "Unknown.*foo-bar",
277                         cmd.main(&ui, args));
278    ATF_REQUIRE(!cmd.executed);
279}
280
281
282ATF_INIT_TEST_CASES(tcs)
283{
284    ATF_ADD_TEST_CASE(tcs, command_proto__parse_cmdline__ok);
285    ATF_ADD_TEST_CASE(tcs, command_proto__parse_cmdline__parse_fail);
286    ATF_ADD_TEST_CASE(tcs, command_proto__parse_cmdline__args_invalid);
287
288    ATF_ADD_TEST_CASE(tcs, base_command__getters);
289    ATF_ADD_TEST_CASE(tcs, base_command__main__ok);
290    ATF_ADD_TEST_CASE(tcs, base_command__main__parse_cmdline_fail);
291
292    ATF_ADD_TEST_CASE(tcs, base_command_no_data__getters);
293    ATF_ADD_TEST_CASE(tcs, base_command_no_data__main__ok);
294    ATF_ADD_TEST_CASE(tcs, base_command_no_data__main__parse_cmdline_fail);
295}
296