CPUnit 0.95 (beta)
The REAL C++ port of JUnit.
|
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 }