1 // what: simple unit test framework 2 // who: developed by Kevlin Henney 3 // when: November 2000 4 // where: tested with BCC 5.5, MSVC 6.0, and g++ 2.91 5 6 #ifndef TEST_INCLUDED 7 #define TEST_INCLUDED 8 9 #include <boost/config.hpp> 10 #include <exception> 11 #include <iostream> 12 #ifdef BOOST_NO_STRINGSTREAM 13 #include <strstream> // for out-of-the-box g++ pre-2.95.3 14 #else 15 #include <sstream> 16 #endif 17 #include <string> 18 19 namespace any_tests // test tuple comprises name and nullary function (object) 20 { 21 template<typename string_type, typename function_type> 22 struct test 23 { 24 string_type name; 25 function_type action; 26 makeany_tests::test27 static test make(string_type name, function_type action) 28 { 29 test result; // MSVC aggreggate initializer bugs 30 result.name = name; 31 result.action = action; 32 return result; 33 } 34 }; 35 } 36 37 namespace any_tests // failure exception used to indicate checked test failures 38 { 39 class failure : public std::exception 40 { 41 public: // struction (default cases are OK) 42 failure(const std::string & why)43 failure(const std::string & why) throw() 44 : reason(why) 45 { 46 } 47 ~failure()48 ~failure() throw() {} 49 50 public: // usage 51 what() const52 virtual const char * what() const throw() 53 { 54 return reason.c_str(); 55 } 56 57 private: // representation 58 59 std::string reason; 60 61 }; 62 } 63 64 namespace any_tests // not_implemented exception used to mark unimplemented tests 65 { 66 class not_implemented : public std::exception 67 { 68 public: // usage (default ctor and dtor are OK) 69 what() const70 virtual const char * what() const throw() 71 { 72 return "not implemented"; 73 } 74 75 }; 76 } 77 78 namespace any_tests // test utilities 79 { check(bool condition,const std::string & description)80 inline void check(bool condition, const std::string & description) 81 { 82 if(!condition) 83 { 84 throw failure(description); 85 } 86 } 87 check_true(bool value,const std::string & description)88 inline void check_true(bool value, const std::string & description) 89 { 90 check(value, "expected true: " + description); 91 } 92 check_false(bool value,const std::string & description)93 inline void check_false(bool value, const std::string & description) 94 { 95 check(!value, "expected false: " + description); 96 } 97 98 template<typename lhs_type, typename rhs_type> check_equal(const lhs_type & lhs,const rhs_type & rhs,const std::string & description)99 void check_equal( 100 const lhs_type & lhs, const rhs_type & rhs, 101 const std::string & description) 102 { 103 check(lhs == rhs, "expected equal values: " + description); 104 } 105 106 template<typename lhs_type, typename rhs_type> check_unequal(const lhs_type & lhs,const rhs_type & rhs,const std::string & description)107 void check_unequal( 108 const lhs_type & lhs, const rhs_type & rhs, 109 const std::string & description) 110 { 111 check(lhs != rhs, "expected unequal values: " + description); 112 } 113 check_null(const void * ptr,const std::string & description)114 inline void check_null(const void * ptr, const std::string & description) 115 { 116 check(!ptr, "expected null pointer: " + description); 117 } 118 check_non_null(const void * ptr,const std::string & description)119 inline void check_non_null(const void * ptr, const std::string & description) 120 { 121 check(ptr != 0, "expected non-null pointer: " + description); 122 } 123 } 124 125 #define TEST_CHECK_THROW(expression, exception, description) \ 126 try \ 127 { \ 128 expression; \ 129 throw ::any_tests::failure(description); \ 130 } \ 131 catch(exception &) \ 132 { \ 133 } 134 135 namespace any_tests // memory tracking (enabled if test new and delete linked in) 136 { 137 class allocations 138 { 139 public: // singleton access 140 instance()141 static allocations & instance() 142 { 143 static allocations singleton; 144 return singleton; 145 } 146 147 public: // logging 148 clear()149 void clear() 150 { 151 alloc_count = dealloc_count = 0; 152 } 153 allocation()154 void allocation() 155 { 156 ++alloc_count; 157 } 158 deallocation()159 void deallocation() 160 { 161 ++dealloc_count; 162 } 163 164 public: // reporting 165 allocated() const166 unsigned long allocated() const 167 { 168 return alloc_count; 169 } 170 deallocated() const171 unsigned long deallocated() const 172 { 173 return dealloc_count; 174 } 175 balanced() const176 bool balanced() const 177 { 178 return alloc_count == dealloc_count; 179 } 180 181 private: // structors (default dtor is fine) 182 allocations()183 allocations() 184 : alloc_count(0), dealloc_count(0) 185 { 186 } 187 188 private: // prevention 189 190 allocations(const allocations &); 191 allocations & operator=(const allocations &); 192 193 private: // state 194 195 unsigned long alloc_count, dealloc_count; 196 197 }; 198 } 199 200 namespace any_tests // tester is the driver class for a sequence of tests 201 { 202 template<typename test_iterator> 203 class tester 204 { 205 public: // structors (default destructor is OK) 206 tester(test_iterator first_test,test_iterator after_last_test)207 tester(test_iterator first_test, test_iterator after_last_test) 208 : begin(first_test), end(after_last_test) 209 { 210 } 211 212 public: // usage 213 214 bool operator()(); // returns true if all tests passed 215 216 private: // representation 217 218 test_iterator begin, end; 219 220 private: // prevention 221 222 tester(const tester &); 223 tester &operator=(const tester &); 224 225 }; 226 227 #if defined(__GNUC__) && defined(__SGI_STL_PORT) && (__GNUC__ < 3) 228 // function scope using declarations don't work: 229 using namespace std; 230 #endif 231 232 template<typename test_iterator> operator ()()233 bool tester<test_iterator>::operator()() 234 { 235 using std::cerr; 236 using std::endl; 237 using std::ends; 238 using std::exception; 239 using std::flush; 240 using std::string; 241 242 unsigned long passed = 0, failed = 0, unimplemented = 0; 243 244 for(test_iterator current = begin; current != end; ++current) 245 { 246 cerr << "[" << current->name << "] " << flush; 247 string result = "passed"; // optimistic 248 249 try 250 { 251 allocations::instance().clear(); 252 current->action(); 253 254 if(!allocations::instance().balanced()) 255 { 256 unsigned long allocated = allocations::instance().allocated(); 257 unsigned long deallocated = allocations::instance().deallocated(); 258 #ifdef BOOST_NO_STRINGSTREAM 259 std::ostrstream report; 260 #else 261 std::ostringstream report; 262 #endif 263 report << "new/delete (" 264 << allocated << " allocated, " 265 << deallocated << " deallocated)" 266 << ends; 267 const string text = report.str(); 268 #ifdef BOOST_NO_STRINGSTREAM 269 report.freeze(false); 270 #endif 271 throw failure(text); 272 } 273 274 ++passed; 275 } 276 catch(const failure & caught) 277 { 278 (result = "failed: ") += caught.what(); 279 ++failed; 280 } 281 catch(const not_implemented &) 282 { 283 result = "not implemented"; 284 ++unimplemented; 285 } 286 catch(const exception & caught) 287 { 288 (result = "exception: ") += caught.what(); 289 ++failed; 290 } 291 catch(...) 292 { 293 result = "failed with unknown exception"; 294 ++failed; 295 } 296 297 cerr << result << endl; 298 } 299 300 cerr << (passed + failed) << " tests: " 301 << passed << " passed, " 302 << failed << " failed"; 303 304 if(unimplemented) 305 { 306 cerr << " (" << unimplemented << " not implemented)"; 307 } 308 309 cerr << endl; 310 311 return failed == 0; 312 } 313 } 314 315 #endif 316 317 // Copyright Kevlin Henney, 2000. All rights reserved. 318 // 319 // Distributed under the Boost Software License, Version 1.0. (See 320 // accompanying file LICENSE_1_0.txt or copy at 321 // http://www.boost.org/LICENSE_1_0.txt) 322