testwave.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*=============================================================================
  2. Boost.Wave: A Standard compliant C++ preprocessor library
  3. http://www.boost.org/
  4. Copyright (c) 2001-2012 Hartmut Kaiser. Distributed under the Boost
  5. Software License, Version 1.0. (See accompanying file
  6. LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. =============================================================================*/
  8. // disable stupid compiler warnings
  9. #include <boost/config/warning_disable.hpp>
  10. // system headers
  11. #include <string>
  12. #include <iostream>
  13. #include <vector>
  14. // include boost
  15. #include <boost/config.hpp>
  16. #include <boost/wave.hpp>
  17. #include <boost/filesystem/path.hpp>
  18. #include <boost/filesystem/operations.hpp>
  19. // test application related headers
  20. #include "cmd_line_utils.hpp"
  21. #include "testwave_app.hpp"
  22. namespace po = boost::program_options;
  23. namespace fs = boost::filesystem;
  24. ///////////////////////////////////////////////////////////////////////////////
  25. //
  26. // The debuglevel command line parameter is used to control the amount of text
  27. // printed by the testwave application.
  28. //
  29. // level 0: prints nothing except serious failures preventing the testwave
  30. // executable from running, the return value of the executable is
  31. // equal to the number of failed tests
  32. // level 1: prints a short summary only
  33. // level 2: prints the names of the failed tests only
  34. // level 3: prints the expected and real result for failed tests
  35. // level 4: prints the outcome of every test
  36. // level 5: prints the real result even for succeeded tests
  37. // level 6: prints the real hooks information recorded, even for succeeded
  38. // tests
  39. //
  40. // level 9: prints information about almost everything
  41. //
  42. // The default debug level is 1.
  43. //
  44. ///////////////////////////////////////////////////////////////////////////////
  45. ///////////////////////////////////////////////////////////////////////////////
  46. int
  47. main(int argc, char *argv[])
  48. {
  49. int error_count = 0;
  50. int config_file_error_count = 0;
  51. try {
  52. // analyze the command line options and arguments
  53. po::options_description desc_cmdline ("Options allowed on the command line");
  54. desc_cmdline.add_options()
  55. ("help,h", "print out program usage (this message)")
  56. ("version,v", "print the version number")
  57. ("copyright,c", "print out the copyright statement")
  58. ("config-file", po::value<std::vector<std::string> >()->composing(),
  59. "specify a config file (alternatively: @arg)")
  60. ("hooks", po::value<bool>()->default_value(true),
  61. "test preprocessing hooks")
  62. ("debug,d", po::value<int>(), "set the debug level (0...9)")
  63. ;
  64. // Hidden options, will be used in in config file analysis to allow to
  65. // recognize positional arguments, will not be shown to the user.
  66. po::options_description desc_hidden("Hidden options");
  67. desc_hidden.add_options()
  68. ("input", po::value<std::vector<std::string> >()->composing(),
  69. "inputfile")
  70. ;
  71. // this is the test application object
  72. po::variables_map vm;
  73. testwave_app app(vm);
  74. // all command line and config file options
  75. po::options_description cmdline_options;
  76. cmdline_options.add(desc_cmdline).add(app.common_options());
  77. // parse command line
  78. // (the (int) cast is to make the True64 compiler happy)
  79. using namespace boost::program_options::command_line_style;
  80. po::parsed_options opts(po::parse_command_line(argc, argv,
  81. cmdline_options, (int)unix_style, cmd_line_utils::at_option_parser));
  82. po::store(opts, vm);
  83. po::notify(vm);
  84. // ... act as required
  85. if (vm.count("help")) {
  86. po::options_description desc_help (
  87. "Usage: testwave [options] [@config-file(s)] file(s)");
  88. desc_help.add(desc_cmdline).add(app.common_options());
  89. std::cout << desc_help << std::endl;
  90. return 0;
  91. }
  92. // debug flag
  93. if (vm.count("debug")) {
  94. int debug_level = vm["debug"].as<int>();
  95. if (debug_level < 0 || debug_level > 9) {
  96. std::cerr
  97. << "testwave: please use an integer in the range [0..9] "
  98. << "as the parameter to the debug option!"
  99. << std::endl;
  100. }
  101. else {
  102. app.set_debuglevel(debug_level);
  103. }
  104. }
  105. if (vm.count("version")) {
  106. return app.print_version();
  107. }
  108. if (vm.count("copyright")) {
  109. return app.print_copyright();
  110. }
  111. // If there is specified at least one config file, parse it and add the
  112. // options to the main variables_map
  113. // Each of the config files is parsed into a separate variables_map to
  114. // allow correct paths handling.
  115. int input_count = 0;
  116. if (vm.count("config-file")) {
  117. std::vector<std::string> const &cfg_files =
  118. vm["config-file"].as<std::vector<std::string> >();
  119. if (9 == app.get_debuglevel()) {
  120. std::cerr << "found " << (unsigned)cfg_files.size()
  121. << " config-file arguments" << std::endl;
  122. }
  123. std::vector<std::string>::const_iterator end = cfg_files.end();
  124. for (std::vector<std::string>::const_iterator cit = cfg_files.begin();
  125. cit != end; ++cit)
  126. {
  127. if (9 == app.get_debuglevel()) {
  128. std::cerr << "reading config_file: " << *cit << std::endl;
  129. }
  130. // parse a single config file and store the results, config files
  131. // may only contain --input and positional arguments
  132. po::variables_map cvm;
  133. if (!cmd_line_utils::read_config_file(app.get_debuglevel(),
  134. *cit, desc_hidden, cvm))
  135. {
  136. if (9 == app.get_debuglevel()) {
  137. std::cerr << "failed to read config_file: " << *cit
  138. << std::endl;
  139. }
  140. ++config_file_error_count;
  141. }
  142. if (9 == app.get_debuglevel()) {
  143. std::cerr << "succeeded to read config_file: " << *cit
  144. << std::endl;
  145. }
  146. // correct the paths parsed into this variables_map
  147. if (cvm.count("input")) {
  148. std::vector<std::string> const &infiles =
  149. cvm["input"].as<std::vector<std::string> >();
  150. if (9 == app.get_debuglevel()) {
  151. std::cerr << "found " << (unsigned)infiles.size()
  152. << " entries" << std::endl;
  153. }
  154. std::vector<std::string>::const_iterator iend = infiles.end();
  155. for (std::vector<std::string>::const_iterator iit = infiles.begin();
  156. iit != iend; ++iit)
  157. {
  158. // correct the file name (pre-pend the config file path)
  159. fs::path cfgpath = boost::wave::util::complete_path(
  160. boost::wave::util::create_path(*cit),
  161. boost::wave::util::current_path());
  162. fs::path filepath =
  163. boost::wave::util::branch_path(cfgpath) /
  164. boost::wave::util::create_path(*iit);
  165. if (9 == app.get_debuglevel()) {
  166. std::cerr << std::string(79, '-') << std::endl;
  167. std::cerr << "executing test: "
  168. << boost::wave::util::native_file_string(filepath)
  169. << std::endl;
  170. }
  171. // execute this unit test case
  172. if (!app.test_a_file(
  173. boost::wave::util::native_file_string(filepath)))
  174. {
  175. if (9 == app.get_debuglevel()) {
  176. std::cerr << "failed to execute test: "
  177. << boost::wave::util::native_file_string(filepath)
  178. << std::endl;
  179. }
  180. ++error_count;
  181. }
  182. else if (9 == app.get_debuglevel()) {
  183. std::cerr << "succeeded to execute test: "
  184. << boost::wave::util::native_file_string(filepath)
  185. << std::endl;
  186. }
  187. ++input_count;
  188. if (9 == app.get_debuglevel()) {
  189. std::cerr << std::string(79, '-') << std::endl;
  190. }
  191. }
  192. }
  193. else if (9 == app.get_debuglevel()) {
  194. std::cerr << "no entries found" << std::endl;
  195. }
  196. }
  197. }
  198. // extract the arguments from the parsed command line
  199. std::vector<po::option> arguments;
  200. std::remove_copy_if(opts.options.begin(), opts.options.end(),
  201. std::back_inserter(arguments), cmd_line_utils::is_argument());
  202. if (9 == app.get_debuglevel()) {
  203. std::cerr << "found " << (unsigned)arguments.size()
  204. << " arguments" << std::endl;
  205. }
  206. // iterate over remaining arguments
  207. std::vector<po::option>::const_iterator arg_end = arguments.end();
  208. for (std::vector<po::option>::const_iterator arg = arguments.begin();
  209. arg != arg_end; ++arg)
  210. {
  211. fs::path filepath(boost::wave::util::create_path((*arg).value[0]));
  212. if (9 == app.get_debuglevel()) {
  213. std::cerr << std::string(79, '-') << std::endl;
  214. std::cerr << "executing test: "
  215. << boost::wave::util::native_file_string(filepath)
  216. << std::endl;
  217. }
  218. if (!app.test_a_file(boost::wave::util::native_file_string(filepath)))
  219. {
  220. if (9 == app.get_debuglevel()) {
  221. std::cerr << "failed to execute test: "
  222. << boost::wave::util::native_file_string(filepath)
  223. << std::endl;
  224. }
  225. ++error_count;
  226. }
  227. else if (9 == app.get_debuglevel()) {
  228. std::cerr << "succeeded to execute test: "
  229. << boost::wave::util::native_file_string(filepath)
  230. << std::endl;
  231. }
  232. if (9 == app.get_debuglevel()) {
  233. std::cerr << std::string(79, '-') << std::endl;
  234. }
  235. ++input_count;
  236. }
  237. // print a message if no input is given
  238. if (0 == input_count) {
  239. std::cerr
  240. << "testwave: no input file specified, "
  241. << "try --help to get a hint."
  242. << std::endl;
  243. return (std::numeric_limits<int>::max)() - 3;
  244. }
  245. else if (app.get_debuglevel() > 0) {
  246. std::cout
  247. << "testwave: " << input_count-error_count
  248. << " of " << input_count << " test(s) succeeded";
  249. if (0 != error_count) {
  250. std::cout
  251. << " (" << error_count << " test(s) failed)";
  252. }
  253. std::cout << "." << std::endl;
  254. }
  255. }
  256. catch (std::exception const& e) {
  257. std::cerr << "testwave: exception caught: " << e.what() << std::endl;
  258. return (std::numeric_limits<int>::max)() - 1;
  259. }
  260. catch (...) {
  261. std::cerr << "testwave: unexpected exception caught." << std::endl;
  262. return (std::numeric_limits<int>::max)() - 2;
  263. }
  264. return error_count + config_file_error_count;
  265. }