OFFIS DCMTK  Version 3.6.0
oftest.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (C) 2011-2012, OFFIS e.V.
4  * All rights reserved. See COPYRIGHT file for details.
5  *
6  * This code is inspired by quicktest.
7  * Copyright (C) 2005-2008
8  * Tyler Streeter (http://www.tylerstreeter.net)
9  * http://quicktest.sourceforge.net
10  *
11  * This software and supporting documentation were developed by
12  *
13  * OFFIS e.V.
14  * R&D Division Health
15  * Escherweg 2
16  * D-26121 Oldenburg, Germany
17  *
18  *
19  * Module: ofstd
20  *
21  * Author: Uli Schlachter
22  *
23  * Purpose: Provide a test framework for the toolkit
24  *
25  */
26 
27 
28 #ifndef OFTEST_H
29 #define OFTEST_H
30 
31 #include "dcmtk/config/osconfig.h"
32 #include "dcmtk/ofstd/ofconapp.h" /* for OFCommandLine */
33 #include "dcmtk/ofstd/ofconsol.h" /* for CERR */
34 #include "dcmtk/ofstd/oflist.h" /* for class OFList */
35 #include "dcmtk/ofstd/ofstream.h" /* for class OFOStringStream */
36 #include "dcmtk/ofstd/ofstring.h" /* for class OFString */
37 #include "dcmtk/ofstd/oftypes.h" /* for OFBool */
38 
39 #ifdef OFTEST_OFSTD_ONLY
40 
41 #define OFTEST_LOG_VERBOSE(msg) do { \
42  if (verbose_) \
43  COUT << msg << OFendl; \
44 } while (0)
45 
46 #else
47 
48 #include "dcmtk/oflog/oflog.h"
49 
50 static OFLogger testLogger = OFLog::getLogger("dcmtk.test");
51 #define OFTEST_LOG_VERBOSE(msg) OFLOG_INFO(testLogger, msg)
52 
53 #endif
54 
61 {
62 public:
65 
69  enum E_Flags {
70  EF_None = 0x0,
72  EF_Slow = 0x1
73  };
74 
78  OFTestTest(const OFString& testName, int flag)
79  : testName_(testName)
80  , results_()
81  , flags_(flag)
82  {
83  }
84 
86  virtual ~OFTestTest()
87  {
88  }
89 
91  int flags() const { return flags_; }
92 
94  const OFString& getTestName() const { return testName_; }
95 
99  const TestResult& runAndReturn()
100  {
101  results_.clear();
102  run();
103  return results_;
104  }
105 
110  virtual void run() = 0;
111 
118  void recordFailure(const OFString& file, unsigned long int line, const OFString& message)
119  {
120  OFOStringStream oss;
121  oss << "FAILED test '" << testName_ << "' at " << file << ":" << line
122  << ": " << message << OFStringStream_ends;
123  OFSTRINGSTREAM_GETOFSTRING(oss, str)
124  results_.push_back(str);
125  }
126 
127 private:
130 
132  TestResult results_;
133 
135  const int flags_;
136 };
137 
142 {
143 public:
146  {
147  static OFTestManager manager;
148  return manager;
149  }
150 
153  {
154  if (!curTest_)
155  abort();
156  return *curTest_;
157  }
158 
162  void addTest(OFTestTest* test)
163  {
164  tests_.push_back(test);
165  }
166 
170  int runTests(const OFList<OFTestTest*>& tests, const char *module)
171  {
172  unsigned int numFailed = 0;
173  OFListConstIterator(OFTestTest*) it;
174  OFString mod_str = module ? " for module '" + OFString(module) + "'" : "";
175 
176  OFTEST_LOG_VERBOSE("Running " << tests.size() << " tests" << mod_str << ":");
177 
178  for (it = tests.begin(); it != tests.end(); ++it)
179  {
180  OFTEST_LOG_VERBOSE(" Running test '" << (*it)->getTestName() << "'...");
181 
182  curTest_ = *it;
183  const OFTestTest::TestResult& result = (*it)->runAndReturn();
184  curTest_ = NULL;
185 
186  if (!result.empty())
187  {
188  numFailed++;
189  OFListConstIterator(OFString) rit;
190  for (rit = result.begin(); rit != result.end(); ++rit)
191  {
192  // recordFailure() already formatted the message
193  CERR << *rit << OFendl;
194  }
195  }
196  }
197 
198  COUT << "Test results" << mod_str << ": "
199  << tests.size() - numFailed << " succeeded, "
200  << numFailed << " failed." << OFendl;
201 
202  /* Only the lowest 8 bit of the exit code can be used! */
203  if (numFailed > 254)
204  {
205  CERR << "WARNING: More than 254 tests failed!" << OFendl;
206  return 254;
207  }
208 
209  return numFailed;
210  }
211 
219  int run(int argc, char* argv[], const char* module)
220  {
221  OFList<OFTestTest*> testsToRun;
222  OFBool listOnly = OFFalse;
223 
224  OFString rcsid;
225 #ifdef OFTEST_OFSTD_ONLY
226  // No proper rcsid because the necessary defines are in dcmdata
227  if (module != NULL)
228  rcsid = "$dcmtk: " + OFString(module) + " $";
229 #else
230  rcsid = "$dcmtk: ";
231  rcsid += OFSTRING_GUARD(module);
232  // skip showing version to compile without dcuid.h
233  // rcsid += " v" OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $";
234 #endif
235 
236  OFConsoleApplication app("tests", "Run the test suite", rcsid.c_str());
237  OFCommandLine cmd;
238  cmd.setParamColumn(13);
239 
240  cmd.addParam("tests-to-run", "names of tests to run (default: all)", OFCmdParam::PM_MultiOptional);
241 
242  cmd.addGroup("general options:");
243  cmd.addOption("--help", "-h", "print this help text and exit", OFCommandLine::AF_Exclusive);
244  cmd.addOption("--list", "-l", "list available tests and exit", OFCommandLine::AF_Exclusive);
245  cmd.addOption("--exhaustive", "-x", "also run extensive and slow tests");
246 #ifdef OFTEST_OFSTD_ONLY
247  cmd.addOption("--verbose", "-v", "verbose mode, print processing details");
248 #else
249  OFLog::addOptions(cmd);
250 #endif
251 
252  /* evaluate command line */
253  if (app.parseCommandLine(cmd, argc, argv))
254  {
255  /* check exclusive options first */
256  }
257 
258 #ifdef OFTEST_OFSTD_ONLY
259  if (cmd.findOption("--verbose")) verbose_ = OFTrue;
260 #else
261  /* We disable warnings and errors by default since some tests cause
262  * such messages by testing corner cases. */
265 #endif
266  if (cmd.findOption("--exhaustive")) exhaustive_ = OFTrue;
267  if (cmd.findOption("--list")) listOnly = OFTrue;
268 
269  if (!buildTestsToRun(cmd, testsToRun))
270  return -1;
271 
272  if (testsToRun.empty())
273  {
274  CERR << "No tests to run!" << OFendl;
275  return 0;
276  }
277 
278  if (listOnly)
279  {
280  OFListIterator(OFTestTest*) it;
281  COUT << "There are " << testsToRun.size() << " tests";
282  if (module)
283  COUT << " for module '" << module << "'";
284  COUT << ":" << OFendl;
285  for (it = testsToRun.begin(); it != testsToRun.end(); ++it)
286  {
287  COUT << " " << (*it)->getTestName() << "\n";
288  }
289  return 0;
290  }
291 
292  return runTests(testsToRun, module);
293  }
294 
295 private:
298  : tests_()
299  , curTest_(NULL)
300  , exhaustive_(OFFalse)
301 #ifdef OFTEST_OFSTD_ONLY
302  , verbose_(OFFalse)
303 #endif
304  {
305  }
306 
308  OFTestManager(const OFTestManager& obj);
309 
312 
319  {
320  const int paramCount = cmd.getParamCount();
321  OFString paramString;
322  OFBool result = OFTrue;
323  if (paramCount == 0)
324  {
325  // If no arguments are given, run all possible tests
326  OFListConstIterator(OFTestTest*) it;
327  for (it = tests_.begin(); it != tests_.end(); ++it)
328  {
329  tests.push_back(*it);
330  }
331  }
332  else
333  {
334  for (int i = 1; i <= paramCount; i++)
335  {
336  cmd.getParam(i, paramString);
337 
338  // Find all tests matching this argument
339  OFBool found = OFFalse;
340  OFListConstIterator(OFTestTest*) it;
341  for (it = tests_.begin(); it != tests_.end(); ++it)
342  {
343  if (testMatches(*it, paramString))
344  {
345  tests.push_back(*it);
346  found = OFTrue;
347  }
348  }
349 
350  if (!found)
351  {
352  CERR << "Error: No test matches '" << paramString << "'" << OFendl;
353  result = OFFalse;
354  }
355  }
356  }
357 
358  // If we are not in exhaustive mode, remove all slow tests
359  if (!exhaustive_)
360  {
361  OFListIterator(OFTestTest*) it = tests.begin();
362  while (it != tests.end())
363  {
364  if ((*it)->flags() & OFTestTest::EF_Slow)
365  it = tests.erase(it);
366  else
367  ++it;
368  }
369  }
370 
371  return result;
372  }
373 
380  OFBool testMatches(const OFTestTest* test, const OFString& str) const
381  {
382  const char* testName = test->getTestName().c_str();
383  const char* string = str.c_str();
384 
385  for (; *testName != '\0' && *string != '\0'; testName++, string++)
386  {
387  // Does the string still match?
388  if (string[0] != '?' && testName[0] != string[0])
389  break;
390  }
391 
392  // Is this a wildcard?
393  // So far we only support '*' at the end of the string
394  if (string[0] == '*' && string[1] == '\0')
395  return OFTrue;
396 
397  // If both strings reached their end, we have a match
398  if (testName[0] == '\0' && string[0] == '\0')
399  return OFTrue;
400  return OFFalse;
401  }
402 
405 
408 
410  OFBool exhaustive_;
411 
412 #ifdef OFTEST_OFSTD_ONLY
413  OFBool verbose_;
415 #endif
416 };
417 
418 
423 #define OFTEST_MAIN(module) \
424 int main(int argc, char* argv[]) \
425 { \
426  return OFTestManager::instance().run(argc, argv, module); \
427 }
428 
430 #define OFTEST_CLASS(testName) \
431 class OFTest ## testName : public OFTestTest \
432 { \
433 public: \
434  OFTest ## testName(); \
435  void run(); \
436 }
437 
443 #define OFTEST_REGISTER_INT(testName) \
444  OFTest ## testName OFTest ## testName ## instance
445 
449 #define OFTEST_REGISTER(testName) \
450  OFTEST_CLASS(testName); \
451  OFTEST_REGISTER_INT(testName)
452 
459 #define OFTEST(testName) OFTEST_FLAGS(testName, EF_None)
460 
467 #define OFTEST_FLAGS(testName, flags) \
468  OFTEST_CLASS(testName); \
469  OFTest ## testName::OFTest ## testName() \
470  : OFTestTest(#testName, flags) \
471  { \
472  OFTestManager::instance().addTest(this); \
473  } \
474  void OFTest ## testName ::run()
475 
481 
485 #define OFCHECK(condition) \
486  do { \
487  if (!(condition)) \
488  OFTestManager::instance().currentTest().recordFailure(__FILE__, __LINE__, #condition); \
489  } while (0)
490 
496 #define OFCHECK_EQUAL(val1, val2) \
497  do { \
498  if ((val1) != (val2)) { \
499  OFOStringStream oss___; \
500  oss___ << "(" << (val1) << ") should equal (" << (val2) << ")" << OFStringStream_ends; \
501  OFSTRINGSTREAM_GETOFSTRING(oss___, str___) \
502  OFTestManager::instance().currentTest().recordFailure(__FILE__, __LINE__, str___); \
503  } \
504  } while (0)
505 
509 #define OFCHECK_FAIL(message) \
510  do { \
511  OFOStringStream oss___; \
512  oss___ << message << OFStringStream_ends; \
513  OFSTRINGSTREAM_GETOFSTRING(oss___, str___) \
514  OFTestManager::instance().currentTest().recordFailure(__FILE__, __LINE__, str___); \
515  } while (0)
516 
518 
519 #endif
OFString testName_
The unique name of this test.
Definition: oftest.h:129
static void configureFromCommandLine(OFCommandLine &cmd, OFConsoleApplication &app)
handle the command line options used for logging
OFList< OFString > TestResult
This is the type used for test results.
Definition: oftest.h:64
static const int AF_Exclusive
exclusive option that overrides any other option (e.g. "--help")
Definition: ofcmdln.h:861
OFBool buildTestsToRun(OFCommandLine &cmd, OFList< OFTestTest * > &tests) const
Build a list of tests which should be executed from the command line.
Definition: oftest.h:318
OFIterator< T > erase(OFIterator< T > position)
removes the element at the given position from the list.
Definition: oflist.h:410
void recordFailure(const OFString &file, unsigned long int line, const OFString &message)
Add a new failure to the result set.
Definition: oftest.h:118
void setParamColumn(const int column)
sets default width of parameter column
TestResult results_
The test results, empty for success.
Definition: oftest.h:132
OFBool empty() const
returns true if list is empty.
Definition: oflist.h:353
static OFLogger getLogger(const char *name)
create a new Logger object
void clear()
removes all elements from the list.
Definition: oflist.h:430
virtual ~OFTestTest()
Destructor.
Definition: oftest.h:86
int runTests(const OFList< OFTestTest * > &tests, const char *module)
Run a list of test cases.
Definition: oftest.h:170
support class for console applications.
Definition: ofconapp.h:49
OFIterator< T > end() const
returns an iterator which points to the past-to-end element of the list.
Definition: oflist.h:348
OFList< OFTestTest * > tests_
List of tests. Statically allocated, so don't have to be freed.
Definition: oftest.h:404
size_t size() const
returns number of elements in the list.
Definition: oflist.h:358
virtual void run()=0
Execute this test case.
parameter is optional, more than one value is allowed (# = 0..n), "[option...]"
Definition: ofcmdln.h:89
The test manager singleton manages the list of available test cases and executes them.
Definition: oftest.h:141
OFTestTest(const OFString &testName, int flag)
Contructor.
Definition: oftest.h:78
OFTestTest * curTest_
Currently running test.
Definition: oftest.h:407
OFTestManager()
Private constructor, this is a singleton!
Definition: oftest.h:297
fatal: very severe error events that will presumably lead the application to abort ...
Definition: oflog.h:70
E_Flags
Special flags that a test can have.
Definition: oftest.h:69
int run(int argc, char *argv[], const char *module)
Handle the given arguments and run the requested test case.
Definition: oftest.h:219
OFIterator< T > begin() const
returns an iterator referencing the first element in the list.
Definition: oflist.h:342
int getParamCount() const
gets number of parameters in the parsed command line.
Definition: ofcmdln.h:416
E_ParamValueStatus getParam(const int pos, OFCmdSignedInt &value)
gets value of specified parameter as signed integer.
static void configure(OFLogger::LogLevel level=OFLogger::WARN_LOG_LEVEL)
set up the logging and enable it
OFBool exhaustive_
Should slow tests be run, too?
Definition: oftest.h:410
A single test case which can be run.
Definition: oftest.h:60
void addTest(OFTestTest *test)
Register a test with this test manager.
Definition: oftest.h:162
OFTestManager & operator=(const OFTestManager &obj)
Private undefined assignment operator.
const OFString & getTestName() const
Definition: oftest.h:94
Slow test which should only be run in exhaustive mode.
Definition: oftest.h:72
simple wrapper around the "low-level" Logger object to make it easier to switch to a different system...
Definition: oflog.h:49
a simple string class that implements a subset of std::string.
Definition: ofstring.h:86
const int flags_
Flags that this test has.
Definition: oftest.h:135
handles command line arguments.
Definition: ofcmdln.h:129
const char * c_str() const
returns a pointer to the initial element of an array of length size()+1 whose first size() elements e...
Definition: ofstring.h:392
static void addOptions(OFCommandLine &cmd)
add the command line options which configureFromCommandLine() checks for
int flags() const
Definition: oftest.h:91
static OFTestManager & instance()
Definition: oftest.h:145
OFBool testMatches(const OFTestTest *test, const OFString &str) const
Test if the test name matches the given name.
Definition: oftest.h:380
const TestResult & runAndReturn()
Execute this test case.
Definition: oftest.h:99
OFTestTest & currentTest()
Definition: oftest.h:152
void push_back(const T &x)
inserts after the last element of the list.
Definition: oflist.h:387


Generated on Wed Dec 28 2016 for OFFIS DCMTK Version 3.6.0 by Doxygen 1.8.8