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 | |
28 | namespace po = boost::program_options; |
29 | namespace 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 | /////////////////////////////////////////////////////////////////////////////// |
54 | int |
55 | main(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 | |