1 // Copyright 2010 Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
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.
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.
29 #include "cli/cmd_help.hpp"
35 #include <atf-c++.hpp>
37 #include "cli/common.ipp"
38 #include "engine/config.hpp"
39 #include "utils/cmdline/commands_map.ipp"
40 #include "utils/cmdline/exceptions.hpp"
41 #include "utils/cmdline/globals.hpp"
42 #include "utils/cmdline/options.hpp"
43 #include "utils/cmdline/parser.hpp"
44 #include "utils/cmdline/ui_mock.hpp"
45 #include "utils/defs.hpp"
46 #include "utils/sanity.hpp"
48 namespace cmdline
= utils::cmdline
;
49 namespace config
= utils::config
;
57 /// Mock command with a simple definition (no options, no arguments).
59 /// Attempting to run this command will result in a crash. It is only provided
60 /// to validate the generation of interactive help.
61 class cmd_mock_simple
: public cli::cli_command
{
63 /// Constructs a new mock command.
65 /// \param name_ The name of the command to create.
66 cmd_mock_simple(const char* name_
) : cli::cli_command(
67 name_
, "", 0, 0, "Simple command")
71 /// Runs the mock command.
73 /// \param unused_ui Object to interact with the I/O of the program.
74 /// \param unused_cmdline Representation of the command line to the
76 /// \param unused_user_config The runtime configuration of the program.
78 /// \return Nothing because this function is never called.
80 run(cmdline::ui
* UTILS_UNUSED_PARAM(ui
),
81 const cmdline::parsed_cmdline
& UTILS_UNUSED_PARAM(cmdline
),
82 const config::tree
& UTILS_UNUSED_PARAM(user_config
))
89 /// Mock command with a complex definition (some options, some arguments).
91 /// Attempting to run this command will result in a crash. It is only provided
92 /// to validate the generation of interactive help.
93 class cmd_mock_complex
: public cli::cli_command
{
95 /// Constructs a new mock command.
97 /// \param name_ The name of the command to create.
98 cmd_mock_complex(const char* name_
) : cli::cli_command(
99 name_
, "[arg1 .. argN]", 0, 2, "Complex command")
101 add_option(cmdline::bool_option("flag_a", "Flag A"));
102 add_option(cmdline::bool_option('b', "flag_b", "Flag B"));
103 add_option(cmdline::string_option('c', "flag_c", "Flag C", "c_arg"));
104 add_option(cmdline::string_option("flag_d", "Flag D", "d_arg", "foo"));
107 /// Runs the mock command.
109 /// \param unused_ui Object to interact with the I/O of the program.
110 /// \param unused_cmdline Representation of the command line to the
112 /// \param unused_user_config The runtime configuration of the program.
114 /// \return Nothing because this function is never called.
116 run(cmdline::ui
* UTILS_UNUSED_PARAM(ui
),
117 const cmdline::parsed_cmdline
& UTILS_UNUSED_PARAM(cmdline
),
118 const config::tree
& UTILS_UNUSED_PARAM(user_config
))
125 /// Initializes the cmdline library and generates the set of test commands.
127 /// \param [out] commands A mapping that is updated to contain the commands to
130 setup(cmdline::commands_map
< cli::cli_command
>& commands
)
132 cmdline::init("progname");
134 commands
.insert(new cmd_mock_simple("mock_simple"));
135 commands
.insert(new cmd_mock_complex("mock_complex"));
137 commands
.insert(new cmd_mock_simple("mock_simple_2"), "First");
138 commands
.insert(new cmd_mock_complex("mock_complex_2"), "First");
140 commands
.insert(new cmd_mock_simple("mock_simple_3"), "Second");
144 /// Performs a test on the global help (not that of a subcommand).
146 /// \param general_options The genral options supported by the tool, if any.
147 /// \param expected_options Expected lines of help output documenting the
148 /// options in general_options.
149 /// \param ui The cmdline::mock_ui object to which to write the output.
151 global_test(const cmdline::options_vector
& general_options
,
152 const std::vector
< std::string
>& expected_options
,
153 cmdline::ui_mock
& ui
)
155 cmdline::commands_map
< cli::cli_command
> mock_commands
;
156 setup(mock_commands
);
158 cmdline::args_vector args
;
159 args
.push_back("help");
161 cmd_help
cmd(&general_options
, &mock_commands
);
162 ATF_REQUIRE_EQ(EXIT_SUCCESS
, cmd
.main(&ui
, args
, engine::default_config()));
164 std::vector
< std::string
> expected
;
166 expected
.push_back("Usage: progname [general_options] command "
167 "[command_options] [args]");
168 if (!general_options
.empty()) {
169 expected
.push_back("");
170 expected
.push_back("Available general options:");
171 std::copy(expected_options
.begin(), expected_options
.end(),
172 std::back_inserter(expected
));
174 expected
.push_back("");
175 expected
.push_back("Generic commands:");
176 expected
.push_back(" mock_complex Complex command.");
177 expected
.push_back(" mock_simple Simple command.");
178 expected
.push_back("");
179 expected
.push_back("First commands:");
180 expected
.push_back(" mock_complex_2 Complex command.");
181 expected
.push_back(" mock_simple_2 Simple command.");
182 expected
.push_back("");
183 expected
.push_back("Second commands:");
184 expected
.push_back(" mock_simple_3 Simple command.");
185 expected
.push_back("");
186 expected
.push_back("See kyua(1) for more details.");
188 ATF_REQUIRE(expected
== ui
.out_log());
189 ATF_REQUIRE(ui
.err_log().empty());
193 } // anonymous namespace
196 ATF_TEST_CASE_WITHOUT_HEAD(global__no_options
);
197 ATF_TEST_CASE_BODY(global__no_options
)
201 cmdline::options_vector general_options
;
203 global_test(general_options
, std::vector
< std::string
>(), ui
);
207 ATF_TEST_CASE_WITHOUT_HEAD(global__some_options
);
208 ATF_TEST_CASE_BODY(global__some_options
)
212 cmdline::options_vector general_options
;
213 const cmdline::bool_option
flag_a("flag_a", "Flag A");
214 general_options
.push_back(&flag_a
);
215 const cmdline::string_option
flag_c('c', "lc", "Flag C", "X");
216 general_options
.push_back(&flag_c
);
218 std::vector
< std::string
> expected
;
219 expected
.push_back(" --flag_a Flag A.");
220 expected
.push_back(" -c X, --lc=X Flag C.");
222 global_test(general_options
, expected
, ui
);
226 ATF_TEST_CASE_WITHOUT_HEAD(subcommand__simple
);
227 ATF_TEST_CASE_BODY(subcommand__simple
)
229 cmdline::options_vector general_options
;
231 cmdline::commands_map
< cli::cli_command
> mock_commands
;
232 setup(mock_commands
);
234 cmdline::args_vector args
;
235 args
.push_back("help");
236 args
.push_back("mock_simple");
238 cmd_help
cmd(&general_options
, &mock_commands
);
240 ATF_REQUIRE_EQ(EXIT_SUCCESS
, cmd
.main(&ui
, args
, engine::default_config()));
241 ATF_REQUIRE(atf::utils::grep_collection("^Usage: progname \\[general_options\\] "
242 "mock_simple$", ui
.out_log()));
243 ATF_REQUIRE(!atf::utils::grep_collection("Available.*options", ui
.out_log()));
244 ATF_REQUIRE(atf::utils::grep_collection("^See kyua-mock_simple\\(1\\) for more "
245 "details.", ui
.out_log()));
246 ATF_REQUIRE(ui
.err_log().empty());
250 ATF_TEST_CASE_WITHOUT_HEAD(subcommand__complex
);
251 ATF_TEST_CASE_BODY(subcommand__complex
)
253 cmdline::options_vector general_options
;
254 const cmdline::bool_option
global_a("global_a", "Global A");
255 general_options
.push_back(&global_a
);
256 const cmdline::string_option
global_c('c', "global_c", "Global C",
258 general_options
.push_back(&global_c
);
260 cmdline::commands_map
< cli::cli_command
> mock_commands
;
261 setup(mock_commands
);
263 cmdline::args_vector args
;
264 args
.push_back("help");
265 args
.push_back("mock_complex");
267 cmd_help
cmd(&general_options
, &mock_commands
);
269 ATF_REQUIRE_EQ(EXIT_SUCCESS
, cmd
.main(&ui
, args
, engine::default_config()));
270 ATF_REQUIRE(atf::utils::grep_collection(
271 "^Usage: progname \\[general_options\\] mock_complex "
272 "\\[command_options\\] \\[arg1 .. argN\\]$", ui
.out_log()));
273 ATF_REQUIRE(atf::utils::grep_collection("Available general options",
275 ATF_REQUIRE(atf::utils::grep_collection("--global_a", ui
.out_log()));
276 ATF_REQUIRE(atf::utils::grep_collection("--global_c=c_global",
278 ATF_REQUIRE(atf::utils::grep_collection("Available command options",
280 ATF_REQUIRE(atf::utils::grep_collection("--flag_a *Flag A",
282 ATF_REQUIRE(atf::utils::grep_collection("-b.*--flag_b *Flag B",
284 ATF_REQUIRE(atf::utils::grep_collection(
285 "-c c_arg.*--flag_c=c_arg *Flag C", ui
.out_log()));
286 ATF_REQUIRE(atf::utils::grep_collection(
287 "--flag_d=d_arg *Flag D.*default.*foo", ui
.out_log()));
288 ATF_REQUIRE(atf::utils::grep_collection(
289 "^See kyua-mock_complex\\(1\\) for more details.", ui
.out_log()));
290 ATF_REQUIRE(ui
.err_log().empty());
294 ATF_TEST_CASE_WITHOUT_HEAD(subcommand__unknown
);
295 ATF_TEST_CASE_BODY(subcommand__unknown
)
297 cmdline::options_vector general_options
;
299 cmdline::commands_map
< cli::cli_command
> mock_commands
;
300 setup(mock_commands
);
302 cmdline::args_vector args
;
303 args
.push_back("help");
304 args
.push_back("foobar");
306 cmd_help
cmd(&general_options
, &mock_commands
);
308 ATF_REQUIRE_THROW_RE(cmdline::usage_error
, "command foobar.*not exist",
309 cmd
.main(&ui
, args
, engine::default_config()));
310 ATF_REQUIRE(ui
.out_log().empty());
311 ATF_REQUIRE(ui
.err_log().empty());
315 ATF_TEST_CASE_WITHOUT_HEAD(invalid_args
);
316 ATF_TEST_CASE_BODY(invalid_args
)
318 cmdline::options_vector general_options
;
320 cmdline::commands_map
< cli::cli_command
> mock_commands
;
321 setup(mock_commands
);
323 cmdline::args_vector args
;
324 args
.push_back("help");
325 args
.push_back("mock_simple");
326 args
.push_back("mock_complex");
328 cmd_help
cmd(&general_options
, &mock_commands
);
330 ATF_REQUIRE_THROW_RE(cmdline::usage_error
, "Too many arguments",
331 cmd
.main(&ui
, args
, engine::default_config()));
332 ATF_REQUIRE(ui
.out_log().empty());
333 ATF_REQUIRE(ui
.err_log().empty());
337 ATF_INIT_TEST_CASES(tcs
)
339 ATF_ADD_TEST_CASE(tcs
, global__no_options
);
340 ATF_ADD_TEST_CASE(tcs
, global__some_options
);
341 ATF_ADD_TEST_CASE(tcs
, subcommand__simple
);
342 ATF_ADD_TEST_CASE(tcs
, subcommand__complex
);
343 ATF_ADD_TEST_CASE(tcs
, subcommand__unknown
);
344 ATF_ADD_TEST_CASE(tcs
, invalid_args
);