• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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