CPUnit 0.95 (beta)
The REAL C++ port of JUnit.
/Users/Shared/Development/cpp/sourceforge/cpunit_095/src/cpunit_EntryPoint.cpp
Go to the documentation of this file.
00001 /*
00002   Copyright (c) 2011 Daniel Bakkelund.
00003   All rights reserved.
00004 
00005   Redistribution and use in source and binary forms, with or without
00006   modification, are permitted provided that the following conditions
00007   are met:
00008   1. Redistributions of source code must retain the above copyright
00009   notice, this list of conditions and the following disclaimer.
00010   2. Redistributions in binary form must reproduce the above copyright
00011   notice, this list of conditions and the following disclaimer in the
00012   documentation and/or other materials provided with the distribution.
00013   3. Neither the name of the copyright holders nor the names of its
00014   contributors may be used to endorse or promote products derived from
00015   this software without specific prior written permission.
00016 
00017   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
00027   THE POSSIBILITY OF SUCH DAMAGE.
00028 */
00029 
00030 #include "cpunit_AssertionException.hpp"
00031 #include "cpunit_TestStore.hpp"
00032 #include "cpunit_StringFlyweightStore.hpp"
00033 #include "cpunit_TestExecutionFacade.hpp"
00034 #include "cpunit_ExecutionReport.hpp"
00035 #include "cpunit_ErrorReportFormat.hpp"
00036 #include "cpunit_RegInfo.hpp"
00037 #include "cpunit_ConformityChecker.hpp"
00038 #include "cpunit_WrongSetupException.hpp"
00039 #include "cpunit_CmdLineParser.hpp"
00040 #include "cpunit_TimeFormat.hpp"
00041 #include "cpunit_trace.hpp"
00042 #include "cpunit_EntryPoint.hpp"
00043 #include "cpunit_impl_BootStream.hpp"
00044 
00045 #include <vector>
00046 #include <string>
00047 #include <iostream>
00048 #include <iomanip>
00049 #include <sstream>
00050 
00051 namespace cpunit {
00052 
00053   namespace {
00054   
00055     using namespace std;
00056   
00057     void usage() {
00058       cout<<endl;
00059       cout<<"Usage: <test_executable> [option]* [test-pattern]*"<<endl;
00060       cout<<"where [test-pattern] is a glob-pattern matching tests to run (the default is '*'), and [option] is any of the following: "<<endl;
00061       cout<<endl;
00062       cout<<"    -h          - Display this message and exit. All other options are ignored."<<endl;
00063       cout<<endl;
00064       cout<<"    -L          - List registered tests matching the patterns and exit. All other options are ignored."<<endl;
00065       cout<<endl;
00066       cout<<"    -v          - Verbose mode."<<endl;
00067       cout<<endl;
00068       cout<<"    -V          - Version information."<<endl;
00069       cout<<endl;
00070       cout<<"    -a          - Robust mode, runs all tests (default is to stop on first error)."<<endl;
00071       cout<<endl;
00072       cout<<"    -f=<format> - Error format specification (used for reporting in robust mode):"<<endl;
00073       cout<<"                   %N - newline"<<endl;
00074       cout<<"                   %T - tab"<<endl;
00075       cout<<"                   %e - error type (OK, FAILURE, ERROR)"<<endl;
00076       cout<<"                   %p - suite name"<<endl;
00077       cout<<"                   %n - test name"<<endl;
00078       cout<<"                   %t - test time"<<endl;
00079       cout<<"                   %f - file name"<<endl;
00080       cout<<"                   %l - line number where test is registered"<<endl;
00081       cout<<"                   %m - error message"<<endl;  
00082       cout<<endl;
00083       cout<<"                   Default is '%p::%n - %m (%ts)%N(Registered at %f:%l)'."<<endl;
00084       cout<<endl;
00085       cout<<"    <pattern>  - Which tests to run. Supports name globbing with wildcards, e.g. 'SomeScope::*::test_*_OK'"<<endl;
00086       cout<<"                 Default is '*'."<<endl;
00087       cout<<endl;
00088       cout<<"Examples:"<<endl;
00089       cout<<"    ./test_runner 'cpunit::*' -- Run all tests in the 'cpunit' namespace."<<endl;
00090       cout<<"    ./test_runner 'unit*' 'io*' -- Run all tests starting with 'unit', and then all tests starting with 'io'."<<endl;
00091       cout<<"    ./test_runner -a            -- Try to run all tests, even if there are errors."<<endl;
00092       cout<<"    ./test_runner -av           -- Run all tests in verbose mode, printing the name of each test just prior to execution."<<endl;
00093       cout<<"    ./test_runner -af=%e%p::%n  -- Run all tests and display error messages as e.g. 'FAILURE MyTests::test_stuff'."<<endl;
00094       cout<<endl;
00095       cout<<"Experimental options:"<<endl;
00096       cout<<endl;
00097       cout<<"    --max-time=<time> - Specify the maximal legal running time for unit tests in seconds."<<endl;
00098       cout<<"                        Decimal number values are accepted, e.g. --max-time=0.5"<<endl;
00099       cout<<"                        Notice that, this will not cause the test to be broken off. All tests will"<<endl;
00100       cout<<"                        execute until completion, and tests that spend longer time that this value"<<endl;
00101       cout<<"                        are reported as FAILED."<<endl;
00102       cout<<endl;
00103       cout<<"                        Default max-time is 1.0e10, i.e. about 317 years."<<endl;
00104       cout<<endl;
00105       cout<<"    --conformity - Check all registered test for name conformities:"<<endl;
00106       cout<<"                   * For match rules, see 'conformity-format' below"<<endl;
00107       cout<<"                   * All test suites must reside in files with reflecting names, i.e. if the suite is named"<<endl;
00108       cout<<"                     'SomeScope::MyTest', the files short name must match 'SomeScope_MyTest.*'"<<endl;
00109       cout<<"                     That is, '::' is exchanged with '_'."<<endl;
00110       cout<<endl;
00111       cout<<"    --conformity-format - Specify how conformity checking is to be performed. Must be glob-patterns."<<endl;
00112       cout<<"                         * There must be three tokens, each separated by pipe symbols"<<endl;
00113       cout<<"                         * First token is used to check suite names"<<endl;
00114       cout<<"                         * Second token is used to check test names"<<endl;
00115       cout<<"                         * Third token is used to check test file extensions"<<endl;
00116       cout<<endl;
00117       cout<<"                           Default is '*Test|test_*|*'."<<endl;
00118     }
00119 
00120     const std::string conformity_token("--conformity");
00121     const std::string conformity_format_token("--conformity-format");
00122     const std::string error_format_token("-f");
00123     const std::string robust_token("-a");
00124     const std::string max_time_token("--max-time");
00125 
00126     void print_version_info() {
00127       cout<<"CPUnit version 0.95 (beta)."<<endl;
00128       cout<<"Get the latest version at http://cpunit.sourceforge.net/"<<endl;
00129     }
00130 
00131     std::vector<std::string> get_conformity_format(const CmdLineParser &p) {
00132       const char sep = '|';
00133       std::vector<std::string> result;
00134       std::ostringstream oss;
00135       const std::string spec = p.value_of<string>(conformity_format_token);
00136       CPUNIT_ITRACE("EntryPoint - Conformity format: '"<<spec<<'\'');
00137       for (std::size_t i = 0; i<spec.length(); i++) {
00138    if (spec[i] == sep) {
00139      result.push_back(oss.str());
00140      oss.str("");
00141    } else {
00142      oss<<spec[i];
00143    }
00144       }
00145       result.push_back(oss.str());
00146       if (result.size() != 3) {
00147    oss.str("");
00148    oss<<"Wrong specification of conformity format: "<<spec;
00149    throw cpunit::WrongSetupException(oss.str());
00150       }
00151       return result;
00152     }
00153       
00154     bool report_result(const std::vector<cpunit::ExecutionReport> &result, const std::string &format, ostream &out) {
00155       CPUNIT_ITRACE("EntryPoint - Reporting result with error report format '"<<format<<'\'');
00156       const cpunit::ErrorReportFormat formatter(format);
00157       int errors = 0;
00158       double time_spent = 0;
00159       for (std::size_t i=0; i<result.size(); i++) {
00160    if (result[i].get_execution_result() != cpunit::ExecutionReport::OK) {
00161      CPUNIT_DTRACE("EntryPoint - Reporting error for "<<result[i].get_test().to_string());
00162      out<<std::endl<<formatter.format(result[i])<<std::endl;
00163      errors++;
00164    }
00165    time_spent += result[i].get_time_spent();
00166       }
00167       out<<std::endl<<"Time: "<<std::setprecision(3)<<cpunit::TimeFormat(time_spent)<<std::endl;
00168       out<<std::endl;
00169       if (errors == 0) {
00170    out<<"OK ("<<result.size()<<" tests)"<<std::endl;
00171       } else {
00172    out<<"FAILURE!!! ("<<errors<<" out of "<<result.size()<<" tests failed)"<<std::endl;
00173       }
00174       return errors == 0;
00175     }
00176 
00177     void list_tests(const std::vector<std::string> &patterns) {
00178       std::vector<cpunit::RegInfo> tests;
00179       for (std::size_t i=0; i<patterns.size(); i++) {
00180    std::vector<cpunit::RegInfo> p_tests = cpunit::TestStore::get_instance().get_tests(patterns[i]);
00181    tests.insert(tests.end(), p_tests.begin(), p_tests.end());
00182       }
00183       for (std::size_t i=0; i<tests.size(); i++) {
00184    std::cout<<tests[i].get_path()<<"::"<<tests[i].get_name()<<std::endl;
00185       }
00186       std::cout<<tests.size()<<" tests in total."<<std::endl;
00187     }
00188 
00189     int conformity_report(const std::vector<std::string> &tests_to_check_patterns, 
00190            const std::string path_pattern, 
00191            const std::string name_pattern, 
00192            const std::string extension_pattern, 
00193            const bool verbose, ostream &out) {
00194       int finds = 0;
00195       for (std::size_t i=0; i<tests_to_check_patterns.size(); ++i) {
00196    std::string tests_to_check_pattern = tests_to_check_patterns[i];
00197    if (verbose) {
00198      out<<"Running conformity check with patterns ["<<path_pattern<<"], ["<<name_pattern<<"], [";
00199      out<<extension_pattern<<"] on tests matching ["<<tests_to_check_pattern<<']'<<std::endl;
00200    }
00201    const vector<cpunit::RegInfo> tests = cpunit::TestStore::get_instance().get_tests(tests_to_check_pattern);
00202    cpunit::ConformityChecker cc(path_pattern, name_pattern);
00203    cc.set_extension(extension_pattern);
00204    for (std::size_t i=0; i<tests.size(); i++) {
00205      if (verbose) {
00206        out<<"Conformity check on "<<tests[i].to_string()<<std::endl;
00207      }
00208      if (!cc.matches(tests[i])) {
00209        out<<"NON CONFORMITY DETECTED:"<<std::endl<<tests[i].to_string()<<std::endl;
00210        finds++;
00211      }
00212    }
00213       }
00214       return finds;
00215     }
00216 
00217     struct AutoDisposer {
00218       ~AutoDisposer() {
00219    CPUNIT_ITRACE("AutoDisposer::~AutoDisposer() - Disposing the TestStore.");
00220    cpunit::TestStore::dispose();
00221    CPUNIT_ITRACE("AutoDisposer::~AutoDisposer() - Disposing the StringFlyweightStore.");
00222    cpunit::StringFlyweightStore::dispose();
00223    CPUNIT_ITRACE("AutoDisposer::~AutoDisposer() - Disposing the BootStream.");
00224    cpunit::impl::BootStream::dispose();
00225       }
00226     };
00227   }
00228 
00233   CmdLineParser get_cmd_line_parser() {
00234     CmdLineParser parser;
00235     parser.add_legal("-h --help -L --list -v --verbose -V --version -a --all -f --conformity --conformity-format --max-time");
00236     const int argc = 3;
00237     const char *defaults[argc] = {
00238       "--conformity-format=*Test|test_*|*",
00239       "-f=%p::%n - %m (%ts)%N(Registered at %f:%l)",
00240       "--max-time=1e+10",
00241     };
00242     parser.parse(argc, defaults);
00243     CPUNIT_ITRACE("EntryPoint - Default arguments:"<<parser.to_string());
00244     return parser;
00245   }
00246 
00247 
00255   int main(const int argc, const char **args) {
00256     CmdLineParser parser = get_cmd_line_parser();
00257     parser.parse(argc-1, args+1);
00258     return main(parser);
00259   }
00260 
00272   int main(const CmdLineParser &parser) {
00273 
00274     AutoDisposer ad;
00275     try {
00276       CPUNIT_ITRACE("EntryPoint - Actual arguments:"<<parser.to_string());
00277 
00278       std::vector<std::string> patterns = parser.program_input();
00279       if (patterns.size() == 0) { 
00280    patterns.push_back("*");
00281       }
00282       
00283       if(parser.has_one_of("-h --help")) {
00284    usage();
00285    return 0;
00286       }
00287       if(parser.has_one_of("-L --list")) {
00288    list_tests(patterns);
00289    return 0;
00290       }
00291       if(parser.has_one_of("-V --version")) {
00292    print_version_info();
00293    return 0;
00294       }
00295       
00296       const bool verbose    = parser.has("-v") || parser.has("--verbose");
00297       const bool robust     = parser.has("-a") || parser.has("--all");
00298       const bool conformity = parser.has("--conformity");
00299       
00300       CPUNIT_ITRACE("EntryPoint - verbose="<<verbose<<" robust="<<robust<<" conformity="<<conformity);
00301       
00302       bool conform = true;
00303       if (conformity) {
00304    std::vector<std::string> conf_patterns = get_conformity_format(parser);
00305    const int num_conformity_breaches = conformity_report(patterns, conf_patterns[0], conf_patterns[1], conf_patterns[2], verbose, std::cout);
00306    std::cout<<"There where "<<num_conformity_breaches<<" conformity breaches."<<std::endl;
00307    conform = num_conformity_breaches == 0;
00308       }
00309 
00310       const std::string report_format = parser.value_of<std::string>(error_format_token);
00311       const double max_time = parser.value_of<double>(max_time_token);
00312       const std::vector<cpunit::ExecutionReport> result = cpunit::TestExecutionFacade().execute(patterns, max_time, verbose, robust);
00313       bool all_well = report_result(result, report_format, std::cout);
00314       
00315       int exit_value = 0;
00316       if (!all_well) {
00317    exit_value |= 0x1;
00318       }
00319       if(!conform) {
00320    exit_value |= 0x2;
00321       }
00322       return exit_value;
00323     } catch (AssertionException &e) {
00324       std::cout<<"Terminated due to AssertionException: "<<std::endl<<e.what()<<std::endl;
00325       return 1;
00326     }
00327   }
00328 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines