| /*============================================================================= |
| Boost.Wave: A Standard compliant C++ preprocessor library |
| http://www.boost.org/ |
| |
| Copyright (c) 2001-2010 Hartmut Kaiser. Distributed under the Boost |
| Software License, Version 1.0. (See accompanying file |
| LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| =============================================================================*/ |
| |
| // disable stupid compiler warnings |
| #include <boost/config/warning_disable.hpp> |
| |
| // system headers |
| #include <string> |
| #include <iostream> |
| #include <vector> |
| |
| // include boost |
| #include <boost/config.hpp> |
| #include <boost/wave.hpp> |
| #include <boost/filesystem/path.hpp> |
| #include <boost/filesystem/operations.hpp> |
| |
| // test application related headers |
| #include "cmd_line_utils.hpp" |
| #include "testwave_app.hpp" |
| |
| namespace po = boost::program_options; |
| namespace fs = boost::filesystem; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // The debuglevel command line parameter is used to control the amount of text |
| // printed by the testwave application. |
| // |
| // level 0: prints nothing except serious failures preventing the testwave |
| // executable from running, the return value of the executable is |
| // equal to the number of failed tests |
| // level 1: prints a short summary only |
| // level 2: prints the names of the failed tests only |
| // level 3: prints the expected and real result for failed tests |
| // level 4: prints the outcome of every test |
| // level 5: prints the real result even for succeeded tests |
| // level 6: prints the real hooks information recorded, even for succeeded |
| // tests |
| // |
| // level 9: prints information about almost everything |
| // |
| // The default debug level is 1. |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| int |
| main(int argc, char *argv[]) |
| { |
| int error_count = 0; |
| int config_file_error_count = 0; |
| try { |
| // analyze the command line options and arguments |
| po::options_description desc_cmdline ("Options allowed on the command line"); |
| desc_cmdline.add_options() |
| ("help,h", "print out program usage (this message)") |
| ("version,v", "print the version number") |
| ("copyright,c", "print out the copyright statement") |
| ("config-file", po::value<std::vector<std::string> >()->composing(), |
| "specify a config file (alternatively: @arg)") |
| ("hooks", po::value<bool>()->default_value(true), |
| "test preprocessing hooks") |
| ("debug,d", po::value<int>(), "set the debug level (0...9)") |
| ; |
| |
| // Hidden options, will be used in in config file analysis to allow to |
| // recognize positional arguments, will not be shown to the user. |
| po::options_description desc_hidden("Hidden options"); |
| desc_hidden.add_options() |
| ("input", po::value<std::vector<std::string> >()->composing(), |
| "inputfile") |
| ; |
| |
| // this is the test application object |
| po::variables_map vm; |
| testwave_app app(vm); |
| |
| // all command line and config file options |
| po::options_description cmdline_options; |
| cmdline_options.add(desc_cmdline).add(app.common_options()); |
| |
| // parse command line |
| // (the (int) cast is to make the True64 compiler happy) |
| using namespace boost::program_options::command_line_style; |
| po::parsed_options opts(po::parse_command_line(argc, argv, |
| cmdline_options, (int)unix_style, cmd_line_utils::at_option_parser)); |
| |
| po::store(opts, vm); |
| po::notify(vm); |
| |
| // ... act as required |
| if (vm.count("help")) { |
| po::options_description desc_help ( |
| "Usage: testwave [options] [@config-file(s)] file(s)"); |
| desc_help.add(desc_cmdline).add(app.common_options()); |
| std::cout << desc_help << std::endl; |
| return 0; |
| } |
| |
| // debug flag |
| if (vm.count("debug")) { |
| int debug_level = vm["debug"].as<int>(); |
| if (debug_level < 0 || debug_level > 9) { |
| std::cerr |
| << "testwave: please use an integer in the range [0..9] " |
| << "as the parameter to the debug option!" |
| << std::endl; |
| } |
| else { |
| app.set_debuglevel(debug_level); |
| } |
| } |
| |
| if (vm.count("version")) { |
| return app.print_version(); |
| } |
| |
| if (vm.count("copyright")) { |
| return app.print_copyright(); |
| } |
| |
| // If there is specified at least one config file, parse it and add the |
| // options to the main variables_map |
| // Each of the config files is parsed into a separate variables_map to |
| // allow correct paths handling. |
| int input_count = 0; |
| if (vm.count("config-file")) { |
| std::vector<std::string> const &cfg_files = |
| vm["config-file"].as<std::vector<std::string> >(); |
| |
| if (9 == app.get_debuglevel()) { |
| std::cerr << "found " << (unsigned)cfg_files.size() |
| << " config-file arguments" << std::endl; |
| } |
| |
| std::vector<std::string>::const_iterator end = cfg_files.end(); |
| for (std::vector<std::string>::const_iterator cit = cfg_files.begin(); |
| cit != end; ++cit) |
| { |
| if (9 == app.get_debuglevel()) { |
| std::cerr << "reading config_file: " << *cit << std::endl; |
| } |
| |
| // parse a single config file and store the results, config files |
| // may only contain --input and positional arguments |
| po::variables_map cvm; |
| if (!cmd_line_utils::read_config_file(app.get_debuglevel(), |
| *cit, desc_hidden, cvm)) |
| { |
| if (9 == app.get_debuglevel()) { |
| std::cerr << "failed to read config_file: " << *cit |
| << std::endl; |
| } |
| ++config_file_error_count; |
| } |
| |
| if (9 == app.get_debuglevel()) { |
| std::cerr << "succeeded to read config_file: " << *cit |
| << std::endl; |
| } |
| |
| // correct the paths parsed into this variables_map |
| if (cvm.count("input")) { |
| std::vector<std::string> const &infiles = |
| cvm["input"].as<std::vector<std::string> >(); |
| |
| if (9 == app.get_debuglevel()) { |
| std::cerr << "found " << (unsigned)infiles.size() |
| << " entries" << std::endl; |
| } |
| |
| std::vector<std::string>::const_iterator iend = infiles.end(); |
| for (std::vector<std::string>::const_iterator iit = infiles.begin(); |
| iit != iend; ++iit) |
| { |
| // correct the file name (pre-pend the config file path) |
| fs::path cfgpath = fs::complete( |
| boost::wave::util::create_path(*cit), |
| boost::wave::util::current_path()); |
| fs::path filepath = |
| boost::wave::util::branch_path(cfgpath) / |
| boost::wave::util::create_path(*iit); |
| |
| if (9 == app.get_debuglevel()) { |
| std::cerr << std::string(79, '-') << std::endl; |
| std::cerr << "executing test: " |
| << boost::wave::util::native_file_string(filepath) |
| << std::endl; |
| } |
| |
| // execute this unit test case |
| if (!app.test_a_file( |
| boost::wave::util::native_file_string(filepath))) |
| { |
| if (9 == app.get_debuglevel()) { |
| std::cerr << "failed to execute test: " |
| << boost::wave::util::native_file_string(filepath) |
| << std::endl; |
| } |
| ++error_count; |
| } |
| else if (9 == app.get_debuglevel()) { |
| std::cerr << "succeeded to execute test: " |
| << boost::wave::util::native_file_string(filepath) |
| << std::endl; |
| } |
| ++input_count; |
| |
| if (9 == app.get_debuglevel()) { |
| std::cerr << std::string(79, '-') << std::endl; |
| } |
| } |
| } |
| else if (9 == app.get_debuglevel()) { |
| std::cerr << "no entries found" << std::endl; |
| } |
| } |
| } |
| |
| // extract the arguments from the parsed command line |
| std::vector<po::option> arguments; |
| std::remove_copy_if(opts.options.begin(), opts.options.end(), |
| std::back_inserter(arguments), cmd_line_utils::is_argument()); |
| |
| if (9 == app.get_debuglevel()) { |
| std::cerr << "found " << (unsigned)arguments.size() |
| << " arguments" << std::endl; |
| } |
| |
| // iterate over remaining arguments |
| std::vector<po::option>::const_iterator arg_end = arguments.end(); |
| for (std::vector<po::option>::const_iterator arg = arguments.begin(); |
| arg != arg_end; ++arg) |
| { |
| fs::path filepath(boost::wave::util::create_path((*arg).value[0])); |
| |
| if (9 == app.get_debuglevel()) { |
| std::cerr << std::string(79, '-') << std::endl; |
| std::cerr << "executing test: " |
| << boost::wave::util::native_file_string(filepath) |
| << std::endl; |
| } |
| |
| if (!app.test_a_file(boost::wave::util::native_file_string(filepath))) |
| { |
| if (9 == app.get_debuglevel()) { |
| std::cerr << "failed to execute test: " |
| << boost::wave::util::native_file_string(filepath) |
| << std::endl; |
| } |
| ++error_count; |
| } |
| else if (9 == app.get_debuglevel()) { |
| std::cerr << "succeeded to execute test: " |
| << boost::wave::util::native_file_string(filepath) |
| << std::endl; |
| } |
| |
| if (9 == app.get_debuglevel()) { |
| std::cerr << std::string(79, '-') << std::endl; |
| } |
| ++input_count; |
| } |
| |
| // print a message if no input is given |
| if (0 == input_count) { |
| std::cerr |
| << "testwave: no input file specified, " |
| << "try --help to get a hint." |
| << std::endl; |
| return (std::numeric_limits<int>::max)() - 3; |
| } |
| else if (app.get_debuglevel() > 0) { |
| std::cout |
| << "testwave: " << input_count-error_count |
| << " of " << input_count << " test(s) succeeded"; |
| if (0 != error_count) { |
| std::cout |
| << " (" << error_count << " test(s) failed)"; |
| } |
| std::cout << "." << std::endl; |
| } |
| } |
| catch (std::exception const& e) { |
| std::cerr << "testwave: exception caught: " << e.what() << std::endl; |
| return (std::numeric_limits<int>::max)() - 1; |
| } |
| catch (...) { |
| std::cerr << "testwave: unexpected exception caught." << std::endl; |
| return (std::numeric_limits<int>::max)() - 2; |
| } |
| |
| return error_count + config_file_error_count; |
| } |