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 
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 
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 
414  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


Generated on Tue Dec 20 2016 for OFFIS DCMTK Version 3.6.0 by Doxygen 1.8.1.2