• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
main(int argc,char * argv[])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(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_cmdline).add(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             cmdline_options, (int)unix_style, cmd_line_utils::at_option_parser));
94 
95         po::store(opts, vm);
96         po::notify(vm);
97 
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;
104             return 0;
105         }
106 
107     // debug flag
108         if (vm.count("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("version")) {
122             return app.print_version();
123         }
124 
125         if (vm.count("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("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(app.get_debuglevel(),
155                     *cit, desc_hidden, 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("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                             boost::wave::util::create_path(*cit),
186                             boost::wave::util::current_path());
187                         fs::path filepath =
188                             boost::wave::util::branch_path(cfgpath) /
189                                 boost::wave::util::create_path(*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(filepath)
195                                       << std::endl;
196                         }
197 
198                     // execute this unit test case
199                         if (!app.test_a_file(
200                               boost::wave::util::native_file_string(filepath)))
201                         {
202                             if (9 == app.get_debuglevel()) {
203                                 std::cerr << "failed to execute test: "
204                                           << boost::wave::util::native_file_string(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(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(opts.options.begin(), opts.options.end(),
230             std::back_inserter(arguments), 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((*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(filepath)
248                           << std::endl;
249             }
250 
251             if (!app.test_a_file(boost::wave::util::native_file_string(filepath)))
252             {
253                 if (9 == app.get_debuglevel()) {
254                     std::cerr << "failed to execute test: "
255                               << boost::wave::util::native_file_string(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(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