1/*=============================================================================
2 Boost.Wave: A Standard compliant C++ preprocessor library
3 http://www.boost.org/
4
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=============================================================================*/
9
10// disable stupid compiler warnings
11#include <boost/config/warning_disable.hpp>
12
13// system headers
14#include <string>
15#include <iostream>
16#include <vector>
17
18// include boost
19#include <boost/config.hpp>
20#include <boost/wave.hpp>
21#include <boost/filesystem/path.hpp>
22#include <boost/filesystem/operations.hpp>
23
24// test application related headers
25#include "cmd_line_utils.hpp"
26#include "testwave_app.hpp"
27
28namespace po = boost::program_options;
29namespace fs = boost::filesystem;
30
31///////////////////////////////////////////////////////////////////////////////
32//
33// The debuglevel command line parameter is used to control the amount of text
34// printed by the testwave application.
35//
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
45// tests
46//
47// level 9: prints information about almost everything
48//
49// The default debug level is 1.
50//
51///////////////////////////////////////////////////////////////////////////////
52
53///////////////////////////////////////////////////////////////////////////////
54int
55main(int argc, char *argv[])
56{
57 int error_count = 0;
58 int config_file_error_count = 0;
59 try {
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(v: true),
69 "test preprocessing hooks")
70 ("debug,d", po::value<int>(), "set the debug level (0...9)")
71 ;
72
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(),
78 "inputfile")
79 ;
80
81 // this is the test application object
82 po::variables_map vm;
83 testwave_app app(vm);
84
85 // all command line and config file options
86 po::options_description cmdline_options;
87 cmdline_options.add(desc: desc_cmdline).add(desc: app.common_options());
88
89 // parse command line
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 desc: cmdline_options, style: (int)unix_style, ext: cmd_line_utils::at_option_parser));
94
95 po::store(options: opts, m&: vm);
96 po::notify(m&: vm);
97
98 // ... act as required
99 if (vm.count(x: "help")) {
100 po::options_description desc_help (
101 "Usage: testwave [options] [@config-file(s)] file(s)");
102 desc_help.add(desc: desc_cmdline).add(desc: app.common_options());
103 std::cout << desc_help << std::endl;
104 return 0;
105 }
106
107 // debug flag
108 if (vm.count(x: "debug")) {
109 int debug_level = vm["debug"].as<int>();
110 if (debug_level < 0 || debug_level > 9) {
111 std::cerr
112 << "testwave: please use an integer in the range [0..9] "
113 << "as the parameter to the debug option!"
114 << std::endl;
115 }
116 else {
117 app.set_debuglevel(debug_level);
118 }
119 }
120
121 if (vm.count(x: "version")) {
122 return app.print_version();
123 }
124
125 if (vm.count(x: "copyright")) {
126 return app.print_copyright();
127 }
128
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.
133 int input_count = 0;
134 if (vm.count(x: "config-file")) {
135 std::vector<std::string> const &cfg_files =
136 vm["config-file"].as<std::vector<std::string> >();
137
138 if (9 == app.get_debuglevel()) {
139 std::cerr << "found " << (unsigned)cfg_files.size()
140 << " config-file arguments" << std::endl;
141 }
142
143 std::vector<std::string>::const_iterator end = cfg_files.end();
144 for (std::vector<std::string>::const_iterator cit = cfg_files.begin();
145 cit != end; ++cit)
146 {
147 if (9 == app.get_debuglevel()) {
148 std::cerr << "reading config_file: " << *cit << std::endl;
149 }
150
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(debuglevel: app.get_debuglevel(),
155 filename: *cit, desc: desc_hidden, vm&: cvm))
156 {
157 if (9 == app.get_debuglevel()) {
158 std::cerr << "failed to read config_file: " << *cit
159 << std::endl;
160 }
161 ++config_file_error_count;
162 }
163
164 if (9 == app.get_debuglevel()) {
165 std::cerr << "succeeded to read config_file: " << *cit
166 << std::endl;
167 }
168
169 // correct the paths parsed into this variables_map
170 if (cvm.count(x: "input")) {
171 std::vector<std::string> const &infiles =
172 cvm["input"].as<std::vector<std::string> >();
173
174 if (9 == app.get_debuglevel()) {
175 std::cerr << "found " << (unsigned)infiles.size()
176 << " entries" << std::endl;
177 }
178
179 std::vector<std::string>::const_iterator iend = infiles.end();
180 for (std::vector<std::string>::const_iterator iit = infiles.begin();
181 iit != iend; ++iit)
182 {
183 // correct the file name (pre-pend the config file path)
184 fs::path cfgpath = boost::wave::util::complete_path(
185 p: boost::wave::util::create_path(p: *cit),
186 base: boost::wave::util::current_path());
187 fs::path filepath =
188 boost::wave::util::branch_path(p: cfgpath) /
189 boost::wave::util::create_path(p: *iit);
190
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(p: filepath)
195 << std::endl;
196 }
197
198 // execute this unit test case
199 if (!app.test_a_file(
200 filename: boost::wave::util::native_file_string(p: filepath)))
201 {
202 if (9 == app.get_debuglevel()) {
203 std::cerr << "failed to execute test: "
204 << boost::wave::util::native_file_string(p: filepath)
205 << std::endl;
206 }
207 ++error_count;
208 }
209 else if (9 == app.get_debuglevel()) {
210 std::cerr << "succeeded to execute test: "
211 << boost::wave::util::native_file_string(p: filepath)
212 << std::endl;
213 }
214 ++input_count;
215
216 if (9 == app.get_debuglevel()) {
217 std::cerr << std::string(79, '-') << std::endl;
218 }
219 }
220 }
221 else if (9 == app.get_debuglevel()) {
222 std::cerr << "no entries found" << std::endl;
223 }
224 }
225 }
226
227 // extract the arguments from the parsed command line
228 std::vector<po::option> arguments;
229 std::remove_copy_if(first: opts.options.begin(), last: opts.options.end(),
230 result: std::back_inserter(x&: arguments), pred: cmd_line_utils::is_argument());
231
232 if (9 == app.get_debuglevel()) {
233 std::cerr << "found " << (unsigned)arguments.size()
234 << " arguments" << std::endl;
235 }
236
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)
241 {
242 fs::path filepath(boost::wave::util::create_path(p: (*arg).value[0]));
243
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(p: filepath)
248 << std::endl;
249 }
250
251 if (!app.test_a_file(filename: boost::wave::util::native_file_string(p: filepath)))
252 {
253 if (9 == app.get_debuglevel()) {
254 std::cerr << "failed to execute test: "
255 << boost::wave::util::native_file_string(p: filepath)
256 << std::endl;
257 }
258 ++error_count;
259 }
260 else if (9 == app.get_debuglevel()) {
261 std::cerr << "succeeded to execute test: "
262 << boost::wave::util::native_file_string(p: filepath)
263 << std::endl;
264 }
265
266 if (9 == app.get_debuglevel()) {
267 std::cerr << std::string(79, '-') << std::endl;
268 }
269 ++input_count;
270 }
271
272 // print a message if no input is given
273 if (0 == input_count) {
274 std::cerr
275 << "testwave: no input file specified, "
276 << "try --help to get a hint."
277 << std::endl;
278 return (std::numeric_limits<int>::max)() - 3;
279 }
280 else if (app.get_debuglevel() > 0) {
281 std::cout
282 << "testwave: " << input_count-error_count
283 << " of " << input_count << " test(s) succeeded";
284 if (0 != error_count) {
285 std::cout
286 << " (" << error_count << " test(s) failed)";
287 }
288 std::cout << "." << std::endl;
289 }
290 }
291 catch (std::exception const& e) {
292 std::cerr << "testwave: exception caught: " << e.what() << std::endl;
293 return (std::numeric_limits<int>::max)() - 1;
294 }
295 catch (...) {
296 std::cerr << "testwave: unexpected exception caught." << std::endl;
297 return (std::numeric_limits<int>::max)() - 2;
298 }
299
300 return error_count + config_file_error_count;
301}
302

source code of boost/libs/wave/test/testwave/testwave.cpp