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