00001 /*!@file TestSuite/TestSuite.C Class to manage a suite of tests */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Rob Peters <rjpeters@klab.caltech.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/TestSuite/TestSuite.C $ 00035 // $Id: TestSuite.C 10854 2009-02-14 05:09:44Z mundhenk $ 00036 // 00037 00038 #ifndef TESTSUITE_C_DEFINED 00039 #define TESTSUITE_C_DEFINED 00040 00041 #include "TestSuite/TestSuite.H" 00042 00043 #include "Image/IO.H" 00044 #include "Image/Image.H" 00045 #include "Image/MathOps.H" 00046 #include "Image/Pixels.H" 00047 #include "rutz/prof.h" 00048 00049 #include <iomanip> 00050 #include <iostream> 00051 #include <sstream> 00052 #include <string> 00053 #include <vector> 00054 00055 /////////////////////////////////////////////////////////////////////// 00056 /* 00057 * 00058 * TestData helper struct just pairs a TestFunc with its name. 00059 * 00060 */ 00061 /////////////////////////////////////////////////////////////////////// 00062 00063 struct TestData 00064 { 00065 TestData(const std::string& n, TestFunc f) : name(n), func(f) {} 00066 00067 std::string name; 00068 TestFunc func; 00069 }; 00070 00071 /////////////////////////////////////////////////////////////////////// 00072 /* 00073 * 00074 * TestSuite::Impl holds all the member variables that TestSuite 00075 * needs. However, by putting them in a private struct and having TestSuite 00076 * just store a pointer to that struct, the header file just needs to see a 00077 * forward declaration of the struct, and does NOT have to #include <vector>, 00078 * <sstream>, etc. 00079 * 00080 */ 00081 /////////////////////////////////////////////////////////////////////// 00082 00083 struct TestSuite::Impl 00084 { 00085 Impl() : itsTests(), itsSuccess(true), itsOutput() {} 00086 00087 std::vector<TestData> itsTests; 00088 bool itsSuccess; 00089 std::ostringstream itsOutput; 00090 00091 void printResult() 00092 { 00093 std::cout << itsSuccess << "\n" 00094 << itsOutput.str() << "\n"; 00095 } 00096 00097 template <class T> 00098 bool requireScalarEq(const T& expr, const T& expected, 00099 const char* srcfile, int line, const char* expr_str) 00100 { 00101 const bool ok = (expr == expected); 00102 if ( !ok ) 00103 { 00104 itsSuccess = false; 00105 itsOutput << "\tFAILED @ " << srcfile << ":" << line << ": " 00106 << expr_str << ", " 00107 << "expected '" 00108 << std::setprecision(30) << std::showpoint << expected 00109 << "', got '" 00110 << std::setprecision(30) << std::showpoint << expr 00111 << "'\n"; 00112 } 00113 return ok; 00114 } 00115 00116 template <class T> 00117 bool requireImgEq(const Image<T>& expr, const Image<T>& expected, 00118 const char* srcfile, int line, const char* expr_str) 00119 { 00120 const bool ok = (expr == expected); 00121 if ( !ok ) 00122 { 00123 itsSuccess = false; 00124 itsOutput << "\tFAILED @ " << srcfile << ":" << line << ": " 00125 << expr_str << ", " 00126 << "expected (" << convertToString(expected.getDims()) << ") " 00127 << expected << ", " 00128 << "got (" << convertToString(expr.getDims()) << ") " 00129 << expr << "\n"; 00130 } 00131 return ok; 00132 } 00133 00134 bool requireImgEqFp(const Image<float>& expr, const Image<float>& expected, 00135 const float prec, const char* srcfile, int line, 00136 const char* expr_str) 00137 { 00138 bool ok = false; 00139 std::ostringstream extrainfo; 00140 00141 if (expr.getDims() == expected.getDims()) 00142 { 00143 Image<float> diff = abs(expr - expected); 00144 float ma; Point2D<int> loc; 00145 findMax(diff, loc, ma); 00146 00147 ok = (ma <= prec); 00148 00149 extrainfo << "(largest difference of " << ma << " at [x=" 00150 << loc.i << ",y=" << loc.j << "])\n"; 00151 } 00152 00153 if ( !ok ) 00154 { 00155 itsSuccess = false; 00156 itsOutput << "\tFAILED @ " << srcfile << ":" << line << ": " 00157 << expr_str << ", " 00158 << "expected (" << convertToString(expected.getDims()) << ") " 00159 << expected << ", " 00160 << "got (" << convertToString(expr.getDims()) << ") " 00161 << expr << "\n" 00162 << extrainfo.str(); 00163 } 00164 return ok; 00165 } 00166 00167 bool requireEqUserTypeImpl(const bool ok, 00168 const std::string& expr, 00169 const std::string& expected, 00170 const char* srcfile, int line, 00171 const char* expr_str) 00172 00173 { 00174 if ( !ok ) 00175 { 00176 itsSuccess = false; 00177 itsOutput << "\tFAILED @ " << srcfile << ":" << line << ": " 00178 << expr_str << ", " 00179 << "expected '" 00180 << expected 00181 << "', got '" 00182 << expr 00183 << "'\n"; 00184 } 00185 return ok; 00186 } 00187 00188 template <class T> 00189 static bool compare(const T& lhs, Op op, const T& rhs) 00190 { 00191 switch (op) 00192 { 00193 case EQ: return lhs == rhs; 00194 case NEQ: return lhs != rhs; 00195 case LT: return lhs < rhs; 00196 case LTE: return lhs <= rhs; 00197 case GT: return lhs > rhs; 00198 case GTE: return lhs >= rhs; 00199 } 00200 LFATAL("invalid Op '%d'", int(op)); 00201 return false; // "can't happen" 00202 } 00203 00204 static const char* opname(Op op) 00205 { 00206 switch (op) 00207 { 00208 case EQ: return " == "; 00209 case NEQ: return " != "; 00210 case LT: return " < "; 00211 case LTE: return " <= "; 00212 case GT: return " > "; 00213 case GTE: return " >= "; 00214 } 00215 LFATAL("invalid Op '%d'", int(op)); 00216 return ""; // "can't happen" 00217 } 00218 00219 template <class T> 00220 bool require(const T& lhs, Op op, const T& rhs, 00221 const char* srcfile, int line, 00222 const char* lhs_str, const char* rhs_str) 00223 { 00224 const bool ok = compare(lhs, op, rhs); 00225 if ( !ok ) 00226 { 00227 itsSuccess = false; 00228 itsOutput << "\tFAILED @ " << srcfile << ":" << line << ":\n" 00229 << "\t expected " << lhs_str << opname(op) << rhs_str << ",\n " 00230 << "\t got " << lhs_str << "==" << lhs 00231 << " and " << rhs_str << "==" << rhs << '\n'; 00232 } 00233 return ok; 00234 } 00235 }; 00236 00237 /////////////////////////////////////////////////////////////////////// 00238 // 00239 // TestSuite member function definitions 00240 // 00241 /////////////////////////////////////////////////////////////////////// 00242 00243 00244 // ###################################################################### 00245 TestSuite::TestSuite() : 00246 rep(new Impl) 00247 {} 00248 00249 // ###################################################################### 00250 TestSuite::~TestSuite() 00251 { 00252 delete rep; 00253 } 00254 00255 // ###################################################################### 00256 void TestSuite::addTest(const char* name, TestFunc func) 00257 { 00258 rep->itsTests.push_back(TestData(name, func)); 00259 } 00260 00261 // ###################################################################### 00262 void TestSuite::printAvailableTests() const 00263 { 00264 for (unsigned int i = 0; i < rep->itsTests.size(); ++i) 00265 std::cout << '{' << i << '\t' << rep->itsTests[i].name << "}\n"; 00266 } 00267 00268 // ###################################################################### 00269 void TestSuite::printAvailableTestsForPerl() const 00270 { 00271 for (unsigned int i = 0; i < rep->itsTests.size(); ++i) 00272 std::cout << i << '\t' << rep->itsTests[i].name << "\n"; 00273 } 00274 00275 // ###################################################################### 00276 void TestSuite::runTest(int test_n, int repeat_n) 00277 { 00278 if (test_n < 0 || (unsigned int) test_n >= rep->itsTests.size()) 00279 return; 00280 00281 for (int i = 0; i < repeat_n; ++i) 00282 rep->itsTests.at(test_n).func(*this); 00283 00284 rep->printResult(); 00285 } 00286 00287 // ###################################################################### 00288 namespace 00289 { 00290 void showUsageAndExit(const char* argv0) 00291 { 00292 std::cerr << "usage: " << argv0 << " <options>\n" 00293 << "available options:\n" 00294 << "\t--query print test names and number (for tcl)\n" 00295 << "\t--perlquery print test names and number (for perl)\n" 00296 << "\t--run <n> run the test number <n>\n" 00297 << "\t--repeat <n> repeat the test <n> times (for profiling, etc.)\n" 00298 << "\t--dump-prof print a profiling summary before exit\n"; 00299 00300 exit(1); 00301 } 00302 } 00303 00304 // ###################################################################### 00305 void TestSuite::parseAndRun(int argc, const char** argv) 00306 { 00307 if (argc == 1) 00308 { 00309 showUsageAndExit(argv[0]); 00310 } 00311 00312 int test_n = -1; 00313 int repeat_n = 1; 00314 bool dump_prof = false; 00315 00316 for (int i = 1; i < argc; ++i) 00317 { 00318 if (strcmp(argv[i], "--run") == 0) 00319 { 00320 if (++i < argc) test_n = atoi(argv[i]); 00321 } 00322 00323 else if (strcmp(argv[i], "--repeat") == 0) 00324 { 00325 if (++i < argc) repeat_n = atoi(argv[i]); 00326 } 00327 00328 else if (strcmp(argv[i], "--query") == 0) 00329 { 00330 printAvailableTests(); 00331 return; 00332 } 00333 00334 else if (strcmp(argv[i], "--perlquery") == 0) 00335 { 00336 printAvailableTestsForPerl(); 00337 return; 00338 } 00339 00340 else if (strcmp(argv[i], "--dump-prof") == 0) 00341 { 00342 dump_prof = true; 00343 } 00344 00345 else 00346 { 00347 showUsageAndExit(argv[0]); 00348 } 00349 } 00350 00351 runTest(test_n, repeat_n); 00352 00353 if (dump_prof) 00354 rutz::prof::print_all_prof_data(std::cerr); 00355 } 00356 00357 // ###################################################################### 00358 bool TestSuite::require(bool expr, const char* srcfile, int line, const char* expr_str) 00359 { 00360 if (!expr) 00361 { 00362 rep->itsSuccess = false; 00363 rep->itsOutput << "\tFAILED @ " << srcfile << ":" << line << ": " 00364 << expr_str << "\n"; 00365 } 00366 return expr; 00367 } 00368 00369 // ###################################################################### 00370 bool TestSuite::requireEq(int expr, int expected, 00371 const char* srcfile, int line, const char* expr_str) 00372 { return rep->requireScalarEq(expr, expected, srcfile, line, expr_str); } 00373 00374 // ###################################################################### 00375 bool TestSuite::requireEq(uint expr, uint expected, 00376 const char* srcfile, int line, const char* expr_str) 00377 { return rep->requireScalarEq(expr, expected, srcfile, line, expr_str); } 00378 00379 // ###################################################################### 00380 bool TestSuite::requireEq(long expr, long expected, 00381 const char* srcfile, int line, const char* expr_str) 00382 { return rep->requireScalarEq(expr, expected, srcfile, line, expr_str); } 00383 00384 // ###################################################################### 00385 bool TestSuite::requireEq(unsigned long expr, unsigned long expected, 00386 const char* srcfile, int line, const char* expr_str) 00387 { return rep->requireScalarEq(expr, expected, srcfile, line, expr_str); } 00388 00389 // ###################################################################### 00390 bool TestSuite::requireEq(double expr, double expected, 00391 const char* srcfile, int line, const char* expr_str) 00392 { return rep->requireScalarEq(expr, expected, srcfile, line, expr_str); } 00393 00394 // ###################################################################### 00395 bool TestSuite::requireEq(const std::string& expr, const std::string& expected, 00396 const char* srcfile, int line, const char* expr_str) 00397 { return rep->requireScalarEq(expr, expected, srcfile, line, expr_str); } 00398 00399 // ###################################################################### 00400 template <class T> 00401 bool TestSuite::requireEq(const Image<T>& expr, const Image<T>& expected, 00402 const char* srcfile, int line, const char* expr_str) 00403 { return rep->requireImgEq(expr, expected, srcfile, line, expr_str); } 00404 00405 // ###################################################################### 00406 bool TestSuite::requireEq(const Image<float>& expr, 00407 const Image<float>& expected, 00408 const float prec, const char* srcfile, int line, 00409 const char* expr_str) 00410 { return rep->requireImgEqFp(expr, expected, prec, srcfile, line, expr_str); } 00411 00412 // ###################################################################### 00413 bool TestSuite::require(int lhs, Op op, int rhs, 00414 const char* srcfile, int line, 00415 const char* lhs_str, const char* rhs_str) 00416 { return rep->require(lhs, op, rhs, srcfile, line, lhs_str, rhs_str); } 00417 00418 // ###################################################################### 00419 bool TestSuite::require(long lhs, Op op, long rhs, 00420 const char* srcfile, int line, 00421 const char* lhs_str, const char* rhs_str) 00422 { return rep->require(lhs, op, rhs, srcfile, line, lhs_str, rhs_str); } 00423 00424 // ###################################################################### 00425 bool TestSuite::require(double lhs, Op op, double rhs, 00426 const char* srcfile, int line, 00427 const char* lhs_str, const char* rhs_str) 00428 { return rep->require(lhs, op, rhs, srcfile, line, lhs_str, rhs_str); } 00429 00430 // ###################################################################### 00431 bool TestSuite::requireEqUserTypeImpl(const bool ok, 00432 const std::string& expr, 00433 const std::string& expected, 00434 const char* srcfile, int line, 00435 const char* expr_str) 00436 { return rep->requireEqUserTypeImpl(ok, expr, expected, srcfile, line, expr_str); } 00437 00438 // Include the explicit instantiations, and make sure that they go in the 00439 // TestSuite:: namespace 00440 #define INST_CLASS TestSuite:: 00441 #include "inst/TestSuite/TestSuite.I" 00442 00443 template bool INST_CLASS requireEq(const Image<int>& expr, const Image<int>& expected, const char* srcfile, int line, const char* expr_str); 00444 00445 /* So things look consistent in everyone's emacs... */ 00446 /* Local Variables: */ 00447 /* indent-tabs-mode: nil */ 00448 /* End: */ 00449 00450 #endif // !TESTSUITE_C_DEFINED