1 /*=============================================================================
2 Boost.Wave: A Standard compliant C++ preprocessor library
5 Copyright (c) 2001-2012 Hartmut Kaiser. Distributed under the Boost
6 Software License, Version 1.0. (See accompanying file
7 LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 =============================================================================*/
10 // disable stupid compiler warnings
11 #include <boost/config/warning_disable.hpp>
19 #include <boost/config.hpp>
20 #include <boost/wave.hpp>
21 #include <boost/filesystem/path.hpp>
22 #include <boost/filesystem/operations.hpp>
24 // test application related headers
25 #include "cmd_line_utils.hpp"
26 #include "testwave_app.hpp"
28 namespace po
= boost::program_options
;
29 namespace fs
= boost::filesystem
;
31 ///////////////////////////////////////////////////////////////////////////////
33 // The debuglevel command line parameter is used to control the amount of text
34 // printed by the testwave application.
36 // level 0: prints nothing except serious failures preventing the testwave
37 // executable from running, the return value of the executable is
38 // equal to the number of failed tests
39 // level 1: prints a short summary only
40 // level 2: prints the names of the failed tests only
41 // level 3: prints the expected and real result for failed tests
42 // level 4: prints the outcome of every test
43 // level 5: prints the real result even for succeeded tests
44 // level 6: prints the real hooks information recorded, even for succeeded
47 // level 9: prints information about almost everything
49 // The default debug level is 1.
51 ///////////////////////////////////////////////////////////////////////////////
53 ///////////////////////////////////////////////////////////////////////////////
55 main(int argc
, char *argv
[])
58 int config_file_error_count
= 0;
60 // analyze the command line options and arguments
61 po::options_description
desc_cmdline ("Options allowed on the command line");
62 desc_cmdline
.add_options()
63 ("help,h", "print out program usage (this message)")
64 ("version,v", "print the version number")
65 ("copyright,c", "print out the copyright statement")
66 ("config-file", po::value
<std::vector
<std::string
> >()->composing(),
67 "specify a config file (alternatively: @arg)")
68 ("hooks", po::value
<bool>()->default_value(true),
69 "test preprocessing hooks")
70 ("debug,d", po::value
<int>(), "set the debug level (0...9)")
73 // Hidden options, will be used in in config file analysis to allow to
74 // recognize positional arguments, will not be shown to the user.
75 po::options_description
desc_hidden("Hidden options");
76 desc_hidden
.add_options()
77 ("input", po::value
<std::vector
<std::string
> >()->composing(),
81 // this is the test application object
85 // all command line and config file options
86 po::options_description cmdline_options
;
87 cmdline_options
.add(desc_cmdline
).add(app
.common_options());
90 // (the (int) cast is to make the True64 compiler happy)
91 using namespace boost::program_options::command_line_style
;
92 po::parsed_options
opts(po::parse_command_line(argc
, argv
,
93 cmdline_options
, (int)unix_style
, cmd_line_utils::at_option_parser
));
98 // ... act as required
99 if (vm
.count("help")) {
100 po::options_description
desc_help (
101 "Usage: testwave [options] [@config-file(s)] file(s)");
102 desc_help
.add(desc_cmdline
).add(app
.common_options());
103 std::cout
<< desc_help
<< std::endl
;
108 if (vm
.count("debug")) {
109 int debug_level
= vm
["debug"].as
<int>();
110 if (debug_level
< 0 || debug_level
> 9) {
112 << "testwave: please use an integer in the range [0..9] "
113 << "as the parameter to the debug option!"
117 app
.set_debuglevel(debug_level
);
121 if (vm
.count("version")) {
122 return app
.print_version();
125 if (vm
.count("copyright")) {
126 return app
.print_copyright();
129 // If there is specified at least one config file, parse it and add the
130 // options to the main variables_map
131 // Each of the config files is parsed into a separate variables_map to
132 // allow correct paths handling.
134 if (vm
.count("config-file")) {
135 std::vector
<std::string
> const &cfg_files
=
136 vm
["config-file"].as
<std::vector
<std::string
> >();
138 if (9 == app
.get_debuglevel()) {
139 std::cerr
<< "found " << (unsigned)cfg_files
.size()
140 << " config-file arguments" << std::endl
;
143 std::vector
<std::string
>::const_iterator end
= cfg_files
.end();
144 for (std::vector
<std::string
>::const_iterator cit
= cfg_files
.begin();
147 if (9 == app
.get_debuglevel()) {
148 std::cerr
<< "reading config_file: " << *cit
<< std::endl
;
151 // parse a single config file and store the results, config files
152 // may only contain --input and positional arguments
153 po::variables_map cvm
;
154 if (!cmd_line_utils::read_config_file(app
.get_debuglevel(),
155 *cit
, desc_hidden
, cvm
))
157 if (9 == app
.get_debuglevel()) {
158 std::cerr
<< "failed to read config_file: " << *cit
161 ++config_file_error_count
;
164 if (9 == app
.get_debuglevel()) {
165 std::cerr
<< "succeeded to read config_file: " << *cit
169 // correct the paths parsed into this variables_map
170 if (cvm
.count("input")) {
171 std::vector
<std::string
> const &infiles
=
172 cvm
["input"].as
<std::vector
<std::string
> >();
174 if (9 == app
.get_debuglevel()) {
175 std::cerr
<< "found " << (unsigned)infiles
.size()
176 << " entries" << std::endl
;
179 std::vector
<std::string
>::const_iterator iend
= infiles
.end();
180 for (std::vector
<std::string
>::const_iterator iit
= infiles
.begin();
183 // correct the file name (pre-pend the config file path)
184 fs::path cfgpath
= boost::wave::util::complete_path(
185 boost::wave::util::create_path(*cit
),
186 boost::wave::util::current_path());
188 boost::wave::util::branch_path(cfgpath
) /
189 boost::wave::util::create_path(*iit
);
191 if (9 == app
.get_debuglevel()) {
192 std::cerr
<< std::string(79, '-') << std::endl
;
193 std::cerr
<< "executing test: "
194 << boost::wave::util::native_file_string(filepath
)
198 // execute this unit test case
199 if (!app
.test_a_file(
200 boost::wave::util::native_file_string(filepath
)))
202 if (9 == app
.get_debuglevel()) {
203 std::cerr
<< "failed to execute test: "
204 << boost::wave::util::native_file_string(filepath
)
209 else if (9 == app
.get_debuglevel()) {
210 std::cerr
<< "succeeded to execute test: "
211 << boost::wave::util::native_file_string(filepath
)
216 if (9 == app
.get_debuglevel()) {
217 std::cerr
<< std::string(79, '-') << std::endl
;
221 else if (9 == app
.get_debuglevel()) {
222 std::cerr
<< "no entries found" << std::endl
;
227 // extract the arguments from the parsed command line
228 std::vector
<po::option
> arguments
;
229 std::remove_copy_if(opts
.options
.begin(), opts
.options
.end(),
230 std::back_inserter(arguments
), cmd_line_utils::is_argument());
232 if (9 == app
.get_debuglevel()) {
233 std::cerr
<< "found " << (unsigned)arguments
.size()
234 << " arguments" << std::endl
;
237 // iterate over remaining arguments
238 std::vector
<po::option
>::const_iterator arg_end
= arguments
.end();
239 for (std::vector
<po::option
>::const_iterator arg
= arguments
.begin();
240 arg
!= arg_end
; ++arg
)
242 fs::path
filepath(boost::wave::util::create_path((*arg
).value
[0]));
244 if (9 == app
.get_debuglevel()) {
245 std::cerr
<< std::string(79, '-') << std::endl
;
246 std::cerr
<< "executing test: "
247 << boost::wave::util::native_file_string(filepath
)
251 if (!app
.test_a_file(boost::wave::util::native_file_string(filepath
)))
253 if (9 == app
.get_debuglevel()) {
254 std::cerr
<< "failed to execute test: "
255 << boost::wave::util::native_file_string(filepath
)
260 else if (9 == app
.get_debuglevel()) {
261 std::cerr
<< "succeeded to execute test: "
262 << boost::wave::util::native_file_string(filepath
)
266 if (9 == app
.get_debuglevel()) {
267 std::cerr
<< std::string(79, '-') << std::endl
;
272 // print a message if no input is given
273 if (0 == input_count
) {
275 << "testwave: no input file specified, "
276 << "try --help to get a hint."
278 return (std::numeric_limits
<int>::max
)() - 3;
280 else if (app
.get_debuglevel() > 0) {
282 << "testwave: " << input_count
-error_count
283 << " of " << input_count
<< " test(s) succeeded";
284 if (0 != error_count
) {
286 << " (" << error_count
<< " test(s) failed)";
288 std::cout
<< "." << std::endl
;
291 catch (std::exception
const& e
) {
292 std::cerr
<< "testwave: exception caught: " << e
.what() << std::endl
;
293 return (std::numeric_limits
<int>::max
)() - 1;
296 std::cerr
<< "testwave: unexpected exception caught." << std::endl
;
297 return (std::numeric_limits
<int>::max
)() - 2;
300 return error_count
+ config_file_error_count
;