Developer Documentation
TestingFramework.hh
Go to the documentation of this file.
1 /* ========================================================================= *
2  * *
3  * OpenMesh *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openmesh.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenMesh. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39  * *
40  * ========================================================================= */
41 
42 
43 
44 #ifndef TESTINGFRAMEWORK_HH
45 #define TESTINGFRAMEWORK_HH
46 // ----------------------------------------------------------------------------
47 
53 // ----------------------------------------------------------------------------
54 
55 #include "Config.hh"
56 #include <iosfwd>
57 #include <sstream>
58 #include <vector>
59 #include <algorithm>
60 #include <stdexcept>
61 #include <OpenMesh/Core/Utils/Noncopyable.hh>
62 
63 // ------------------------------------------------------------- namespace ----
64 
65 namespace OpenMesh { // BEGIN_NS_OPENMESH
66 namespace Utils { // BEGIN_NS_UTILS
67 
68 
69 // ----------------------------------------------------------------- class ----
70 //
71 // Usage Example
72 //
73 // #include <iostream>
74 // #include <.../TestingFramework.hh>
75 //
76 // struct test_func : public TestingFramework::TestFunc
77 // {
78 // typedef test_func Self;
79 //
80 // // define ctor and copy-ctor
81 // test_func( TestingFramework& _th, std::string _n ) : TestingFramework::TestFunc( _th, _n ) { }
82 // test_func( Self& _cpy ) : TestingFramework::TestFunc(_cpy) { }
83 //
84 // // overload body()
85 // void body()
86 // {
87 //
88 // // Do the tests
89 // // direct call to verify
90 // verify( testResult, expectedResult, "additional information" );
91 //
92 // // or use the define TH_VERIFY. The test-expression will be used as the message string
93 // TH_VERIFY( testResult, expectedResult );
94 //
95 // ...
96 // }
97 // };
98 //
99 // int main(...)
100 // {
101 // TestingFramework testSuite(std::cout); // send output to stdout
102 //
103 // new test_func(testSuite); // create new test instance. It registers with testSuite.
104 // return testSuite.run();
105 // }
106 //
107 
108 //
109 #define TH_VERIFY( expr, expt ) \
110  verify( expr, expt, #expr )
111 
112 //
113 #define TH_VERIFY_X( expr, expt ) \
114  verify_x( expr, expt, #expr )
115 
120 {
121 public:
122 
123  typedef TestingFramework Self;
124  typedef std::logic_error verify_error;
125 
126 #ifndef DOXY_IGNORE_THIS
127  class TestFunc
128  {
129  public:
130  TestFunc( TestingFramework& _th, const std::string& _n )
131  : th_(_th), name_(_n)
132  {
133  th_.reg(this);
134  }
135 
136  virtual ~TestFunc()
137  { }
138 
139  void operator() ( void )
140  {
141  prolog();
142  try
143  {
144  body();
145  }
146  catch( std::exception& x )
147  {
148  std::cerr << "<<Error>>: Cannot proceed test due to failure of last"
149  << " test: " << x.what() << std::endl;
150  }
151  catch(...)
152  {
153  std::cerr << "Fatal: cannot proceed test due to unknown error!"
154  << std::endl;
155  }
156  epilog();
157  }
158 
159  const TestingFramework& testHelper() const { return th_; }
160 
161  protected:
162 
163  virtual void prolog(void)
164  {
165  begin(name_);
166  }
167 
168  virtual void body(void) = 0;
169 
170  virtual void epilog(void)
171  {
172  end();
173  }
174 
175  protected:
176 
177  TestingFramework& testHelper() { return th_; }
178 
179  TestFunc( const TestFunc& _cpy ) : th_(_cpy.th_), name_(_cpy.name_) { }
180 
181 
182  // Use the following method in prolog()
183  TestFunc& begin(std::string _title, const std::string& _info = "")
184  { th_.begin(_title,_info); return *this; }
185 
186 
187  // Use the following method in epilog()
188  TestFunc& end(void)
189  { th_.end(); return *this; }
190 
191 
192  // Use the followin methods in body()
193 
194  template <typename ValueType>
195  bool
196  verify( const ValueType& _rc, const ValueType& _expected,
197  std::string _info )
198  { return th_.verify( _rc, _expected, _info ); }
199 
200  template <typename ValueType>
201  void
202  verify_x( const ValueType& _rc, const ValueType& _expected,
203  std::string _info )
204  {
205  if ( !verify(_rc, _expected, _info) )
206  throw verify_error(_info);
207  }
208 
209  TestFunc& info(const std::string& _info)
210  { th_.info(_info); return *this; }
211 
212  TestFunc& info(const std::ostringstream& _ostr)
213  { th_.info(_ostr); return *this; }
214 
215  private:
216  TestFunc();
217 
218  protected:
219  TestingFramework& th_;
220  std::string name_;
221  };
222 #endif
223 
224  typedef TestFunc* TestFuncPtr;
225  typedef std::vector<TestFuncPtr> TestSet;
226 
227 public:
228 
229  TestingFramework(std::ostream& _os)
230  : errTotal_(0), errCount_(0),
231  verifyTotal_(0), verifyCount_(0),
232  testTotal_(0), testCount_(0),
233  os_(_os)
234  { }
235 
236 protected:
237 
238 #ifndef DOXY_IGNORE_THIS
239  struct TestDeleter
240  {
241  void operator() (TestFuncPtr _tfptr) { delete _tfptr; }
242  };
243 #endif
244 
245 public:
246 
247  virtual ~TestingFramework()
248  {
249  std::for_each(tests_.begin(), tests_.end(), TestDeleter() );
250  }
251 
252 public:
253 
254  template <typename ValueType>
255  bool verify(const ValueType& _rc,
256  const ValueType& _expected,
257  const std::string& _info)
258  {
259  ++verifyTotal_;
260  if ( _rc == _expected )
261  {
262  os_ << " " << _info << ", result: " << _rc << ", OK!" << std::endl;
263  return true;
264  }
265  ++errTotal_;
266  os_ << " " << _info << ", result: " << _rc << " != " << _expected
267  << " <<ERROR>>" << std::endl;
268  return false;
269  }
270 
271  Self& begin(std::string _title, const std::string& _info = "")
272  {
273  std::ostringstream ostr;
274 
275  testTitle_ = _title;
276  errCount_ = errTotal_;
277  ++testTotal_;
278 
279  ostr << _title;
280  if ( !_info.empty() )
281  ostr << " ["<< _info << "]";
282  testTitle_ = ostr.str();
283  os_ << "Begin " << testTitle_ << std::endl;
284  return *this;
285  }
286 
287  Self& end()
288  {
289  if (errorCount()==0)
290  ++testCount_;
291 
292  os_ << "End " << testTitle_ << ": " << errorCount() << " Error(s)." << std::endl;
293  return *this;
294  }
295 
296  Self& info(const std::string& _info)
297  {
298  os_ << " + " << _info << std::endl;
299  return *this;
300  }
301 
302  Self& info(const std::ostringstream& _ostr)
303  {
304  os_ << " + " << _ostr.str() << std::endl;
305  return *this;
306  }
307 
308  size_t errorTotal() const { return errTotal_; }
309  size_t errorCount() const { return errTotal_ - errCount_; }
310  size_t verifyTotal() const { return verifyTotal_; }
311  size_t verifyCount() const { return verifyTotal_ - verifyCount_; }
312  size_t goodTotal() const { return verifyTotal() - errorTotal(); }
313  size_t goodCount() const { return verifyCount() - errorCount(); }
314 
315  size_t testTotal() const { return testTotal_; }
316  size_t testCount() const { return testCount_; }
317 
318 public:
319 
320  int run(void)
321  {
322  os_ << "Test started\n";
323  TestRunner executer;
324  std::for_each(tests_.begin(), tests_.end(), executer );
325  os_ << std::endl;
326  os_ << "All tests completed" << std::endl
327  << " #Tests: " << testCount() << "/" << testTotal() << std::endl
328  << " #Errors: " << errorTotal() << "/" << verifyTotal() << std::endl;
329  return errorTotal();
330  }
331 
332 protected:
333 
334 #ifndef DOXY_IGNORE_THIS
335  struct TestRunner
336  {
337  void operator() (TestFuncPtr _tfptr) { (*_tfptr)(); }
338  };
339 #endif
340 
341  int reg(TestFuncPtr _tfptr)
342  {
343  tests_.push_back(_tfptr);
344  return true;
345  }
346 
347  friend class TestFunc;
348 
349 private:
350 
351  size_t errTotal_;
352  size_t errCount_;
353  size_t verifyTotal_;
354  size_t verifyCount_;
355  size_t testTotal_; // #Tests
356  size_t testCount_; // #Tests ohne Fehler
357 
358  std::string testTitle_;
359  std::ostream& os_;
360 
361  TestSet tests_;
362 
363 };
364 
365 // ============================================================================
366 } // END_NS_UTILS
367 } // END_NS_OPENMESH
368 // ============================================================================
369 #endif // TESTINGFRMEWORK_HH
370 // ============================================================================