A Discrete-Event Network Simulator
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
test.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2009 University of Washington
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */
18 
19 #include "test.h"
20 #include "assert.h"
21 #include "abort.h"
22 #include "system-path.h"
23 #include <math.h>
24 #include <string.h>
25 #include <vector>
26 #include <list>
27 
28 
29 namespace ns3 {
30 
31 bool
32 TestDoubleIsEqual (const double x1, const double x2, const double epsilon)
33 {
34  int exponent;
35  double delta, difference;
36 
37  //
38  // Find exponent of largest absolute value
39  //
40  {
41  double max = (fabs (x1) > fabs (x2)) ? x1 : x2;
42  (void)frexp (max, &exponent);
43  }
44 
45  //
46  // Form a neighborhood of size 2 * delta
47  //
48  delta = ldexp (epsilon, exponent);
49  difference = x1 - x2;
50 
51  if (difference > delta || difference < -delta)
52  {
53  return false;
54  }
55  return true;
56 }
57 
59 {
60  TestCaseFailure (std::string _cond, std::string _actual,
61  std::string _limit, std::string _message,
62  std::string _file, int32_t _line);
63  std::string cond;
64  std::string actual;
65  std::string limit;
66  std::string message;
67  std::string file;
68  int32_t line;
69 };
71 {
72  Result ();
74  std::vector<TestCaseFailure> failure;
76 };
77 
79 {
80 public:
81  void AddTestSuite (TestSuite *testSuite);
82  void StartTestCase (std::string name);
83  void EndTestCase (void);
84  void ReportTestFailure (std::string cond, std::string actual,
85  std::string limit, std::string message,
86  std::string file, int32_t line);
87  bool MustAssertOnFailure (void) const;
88  bool MustContinueOnFailure (void) const;
89  bool MustUpdateData (void) const;
90  std::string GetTopLevelSourceDir (void) const;
91  std::string GetTempDir (void) const;
92 
93  int Run (int argc, char *argv[]);
94 
95  static TestRunnerImpl *Instance (void);
96 
97 private:
98  TestRunnerImpl ();
99  ~TestRunnerImpl ();
100 
101  bool IsTopLevelSourceDir (std::string path) const;
102  std::string ReplaceXmlSpecialCharacters (std::string xml) const;
103  void PrintReport (TestCase *test, std::ostream *os, bool xml, int level);
104  void PrintTestNameList (std::list<TestCase *>::const_iterator begin,
105  std::list<TestCase *>::const_iterator end) const;
106  void PrintTestTypeList (void) const;
107  void PrintHelp (const char *programName) const;
108  std::list<TestCase *> FilterTests (std::string testName, enum TestSuite::Type testType) const;
109 
110 
111  typedef std::vector<TestSuite *> TestSuiteVector;
112 
114  std::string m_tempDir;
115  bool m_verbose;
119 };
120 
121 
122 
123 TestCaseFailure::TestCaseFailure (std::string _cond, std::string _actual,
124  std::string _limit, std::string _message,
125  std::string _file, int32_t _line)
126  : cond (_cond), actual (_actual), limit (_limit),
127  message (_message), file (_file), line (_line)
128 {}
130  : childrenFailed (false)
131 {}
132 
133 
134 
135 TestCase::TestCase (std::string name)
136  : m_parent (0),
137  m_dataDir (""),
138  m_runner (0),
139  m_result (0),
140  m_name (name)
141 {
142 }
143 
145 {
146  NS_ASSERT (m_runner == 0);
147  m_parent = 0;
148  delete m_result;
149  for (std::vector<TestCase *>::const_iterator i = m_children.begin (); i != m_children.end (); ++i)
150  {
151  delete *i;
152  }
153  m_children.clear ();
154 }
155 
156 void
158 {
159  m_children.push_back (testCase);
160  testCase->m_parent = this;
161 
162  std::string::size_type slash, antislash;
163  slash = testCase->m_name.find ("/");
164  antislash = testCase->m_name.find ("\\");
165  if (slash != std::string::npos || antislash != std::string::npos)
166  {
167  std::string fullname = testCase->m_name;
168  TestCase *current = testCase->m_parent;
169  while (current != 0)
170  {
171  fullname = current->m_name + "/" + fullname;
172  current = current->m_parent;
173  }
174  if (slash != std::string::npos)
175  {
176  NS_FATAL_ERROR ("Invalid test name: cannot contain slashes: \"" << fullname << "\"");
177  }
178  if (antislash != std::string::npos)
179  {
180  NS_FATAL_ERROR ("Invalid test name: cannot contain antislashes: \"" << fullname << "\"");
181  }
182  }
183 }
184 
185 bool
186 TestCase::IsFailed (void) const
187 {
188  return m_result->childrenFailed || !m_result->failure.empty ();
189 }
190 
191 void
193 {
194  m_result = new Result ();
195  m_runner = runner;
196  DoSetup ();
197  m_result->clock.Start ();
198  for (std::vector<TestCase *>::const_iterator i = m_children.begin (); i != m_children.end (); ++i)
199  {
200  TestCase *test = *i;
201  test->Run (runner);
202  if (IsFailed ())
203  {
204  goto out;
205  }
206  }
207  DoRun ();
208  out:
209  m_result->clock.End ();
210  DoTeardown ();
211  m_runner = 0;
212 }
213 std::string
214 TestCase::GetName (void) const
215 {
216  return m_name;
217 }
218 void
219 TestCase::ReportTestFailure (std::string cond, std::string actual,
220  std::string limit, std::string message,
221  std::string file, int32_t line)
222 {
223  m_result->failure.push_back (TestCaseFailure (cond, actual, limit,
224  message, file, line));
225  // set childrenFailed flag on parents.
226  TestCase *current = m_parent;
227  while (current != 0)
228  {
229  current->m_result->childrenFailed = true;
230  current = current->m_parent;
231  }
232 
233 }
234 bool
236 {
237  return m_runner->MustAssertOnFailure ();
238 }
239 bool
241 {
242  return m_runner->MustContinueOnFailure ();
243 }
244 
245 std::string
246 TestCase::CreateDataDirFilename (std::string filename)
247 {
248  const TestCase *current = this;
249  while (current->m_dataDir == "" && current != 0)
250  {
251  current = current->m_parent;
252  }
253  if (current == 0)
254  {
255  NS_FATAL_ERROR ("No one called SetDataDir prior to calling this function");
256  }
257 
258  std::string a = SystemPath::Append (m_runner->GetTopLevelSourceDir (), current->m_dataDir);
259  std::string b = SystemPath::Append (a, filename);
260  return b;
261 }
262 std::string
263 TestCase::CreateTempDirFilename (std::string filename)
264 {
265  if (m_runner->MustUpdateData ())
266  {
267  return CreateDataDirFilename (filename);
268  }
269  else
270  {
271  std::list<std::string> names;
272  const TestCase *current = this;
273  while (current != 0)
274  {
275  names.push_front (current->m_name);
276  current = current->m_parent;
277  }
278  std::string tempDir = SystemPath::Append (m_runner->GetTempDir (), SystemPath::Join (names.begin (), names.end ()));
279  SystemPath::MakeDirectories (tempDir);
280  return SystemPath::Append (tempDir, filename);
281  }
282 }
283 bool
285 {
286  return IsStatusFailure ();
287 }
288 bool
290 {
291  return !IsStatusSuccess ();
292 }
293 bool
295 {
296  return m_result->failure.empty ();
297 }
298 
299 void
300 TestCase::SetDataDir (std::string directory)
301 {
302  m_dataDir = directory;
303 }
304 
305 void
307 {}
308 void
310 {}
311 
312 
313 TestSuite::TestSuite (std::string name, TestSuite::Type type)
314  : TestCase (name),
315  m_type (type)
316 {
318 }
319 
322 {
323  return m_type;
324 }
325 
326 void
328 {}
329 
331  : m_tempDir (""),
332  m_assertOnFailure (false),
333  m_continueOnFailure (true),
334  m_updateData (false)
335 {
336 }
337 
339 {
340 }
341 
342 
343 
346 {
347  static TestRunnerImpl runner;
348  return &runner;
349 }
350 
351 void
353 {
354  m_suites.push_back (testSuite);
355 }
356 
357 
358 bool
360 {
361  return m_assertOnFailure;
362 }
363 bool
365 {
366  return m_continueOnFailure;
367 }
368 
369 bool
371 {
372  return m_updateData;
373 }
374 std::string
376 {
377  return m_tempDir;
378 }
379 bool
380 TestRunnerImpl::IsTopLevelSourceDir (std::string path) const
381 {
382  bool haveVersion = false;
383  bool haveLicense = false;
384 
385  //
386  // If there's a file named VERSION and a file named LICENSE in this
387  // directory, we assume it's our top level source directory.
388  //
389 
390  std::list<std::string> files = SystemPath::ReadFiles (path);
391  for (std::list<std::string>::const_iterator i = files.begin (); i != files.end (); ++i)
392  {
393  if (*i == "VERSION")
394  {
395  haveVersion = true;
396  }
397  else if (*i == "LICENSE")
398  {
399  haveLicense = true;
400  }
401  }
402 
403  return haveVersion && haveLicense;
404 }
405 
406 std::string
408 {
409  std::string self = SystemPath::FindSelfDirectory ();
410  std::list<std::string> elements = SystemPath::Split (self);
411  while (!elements.empty ())
412  {
413  std::string path = SystemPath::Join (elements.begin (), elements.end ());
414  if (IsTopLevelSourceDir (path))
415  {
416  return path;
417  }
418  elements.pop_back ();
419  }
420  NS_FATAL_ERROR ("Could not find source directory from self=" << self);
421 }
422 
423 //
424 // XML files have restrictions on certain characters that may be present in
425 // data. We need to replace these characters with their alternate
426 // representation on the way into the XML file.
427 //
428 std::string
430 {
431  std::string specials = "<>&\"'";
432  std::string replacements[] = {"&lt;", "&gt;", "&amp;", "&#39;", "&quot;"};
433  std::string result;
434  std::size_t index, length = xml.length ();
435 
436  for (size_t i = 0; i < length; ++i)
437  {
438  char character = xml[i];
439 
440  if ((index = specials.find (character)) == std::string::npos)
441  {
442  result.push_back (character);
443  }
444  else
445  {
446  result += replacements[index];
447  }
448  }
449  return result;
450 }
451 
452 struct Indent
453 {
454  Indent (int level);
455  int level;
456 };
457 Indent::Indent (int _level)
458  : level (_level)
459 {}
460 std::ostream &operator << (std::ostream &os, const Indent &val)
461 {
462  for (int i = 0; i < val.level; i++)
463  {
464  os << " ";
465  }
466  return os;
467 }
468 
469 void
470 TestRunnerImpl::PrintReport (TestCase *test, std::ostream *os, bool xml, int level)
471 {
472  if (test->m_result == 0)
473  {
474  // Do not print reports for tests that were not run.
475  return;
476  }
477  // Report times in seconds, from ms timer
478  const double MS_PER_SEC = 1000.;
479  double real = test->m_result->clock.GetElapsedReal () / MS_PER_SEC;
480  double user = test->m_result->clock.GetElapsedUser () / MS_PER_SEC;
481  double system = test->m_result->clock.GetElapsedSystem () / MS_PER_SEC;
482 
483  (*os).precision (3);
484  *os << std::fixed;
485 
486  std::string statusString = test->IsFailed ()?"FAIL":"PASS";
487  if (xml)
488  {
489  *os << Indent (level) << "<Test>" << std::endl;
490  *os << Indent (level+1) << "<Name>" << ReplaceXmlSpecialCharacters (test->m_name)
491  << "</Name>" << std::endl;
492  *os << Indent (level+1) << "<Result>" << statusString << "</Result>" << std::endl;
493  *os << Indent (level+1) << "<Time real=\"" << real << "\" user=\"" << user
494  << "\" system=\"" << system << "\"/>" << std::endl;
495  for (uint32_t i = 0; i < test->m_result->failure.size (); i++)
496  {
497  TestCaseFailure failure = test->m_result->failure[i];
498  *os << Indent (level+2) << "<FailureDetails>" << std::endl
499  << Indent (level+3) << "<Condition>"
500  << ReplaceXmlSpecialCharacters (failure.cond) << "</Condition>" << std::endl
501  << Indent (level+3) << "<Actual>"
502  << ReplaceXmlSpecialCharacters (failure.actual) << "</Actual>" << std::endl
503  << Indent (level+3) << "<Limit>"
504  << ReplaceXmlSpecialCharacters (failure.limit) << "</Limit>" << std::endl
505  << Indent (level+3) << "<Message>"
506  << ReplaceXmlSpecialCharacters (failure.message) << "</Message>" << std::endl
507  << Indent (level+3) << "<File>"
508  << ReplaceXmlSpecialCharacters (failure.file) << "</File>" << std::endl
509  << Indent (level+3) << "<Line>" << failure.line << "</Line>" << std::endl
510  << Indent (level+2) << "</FailureDetails>" << std::endl;
511  }
512  for (uint32_t i = 0; i < test->m_children.size (); i++)
513  {
514  TestCase *child = test->m_children[i];
515  PrintReport (child, os, xml, level + 1);
516  }
517  *os << Indent (level) << "</Test>" << std::endl;
518  }
519  else
520  {
521  *os << Indent (level) << statusString << " " << test->GetName ()
522  << " " << real << " s" << std::endl;
523  if (m_verbose)
524  {
525  for (uint32_t i = 0; i < test->m_result->failure.size (); i++)
526  {
527  TestCaseFailure failure = test->m_result->failure[i];
528  *os << Indent (level) << " got=\"" << failure.cond << "\" expected=\""
529  << failure.actual << "\" in=\"" << failure.file << ":" << failure.line
530  << "\" " << failure.message << std::endl;
531  }
532  for (uint32_t i = 0; i < test->m_children.size (); i++)
533  {
534  TestCase *child = test->m_children[i];
535  PrintReport (child, os, xml, level + 1);
536  }
537  }
538  }
539 }
540 
541 void
542 TestRunnerImpl::PrintHelp (const char *program_name) const
543 {
544  std::cout << "Usage: " << program_name << " [OPTIONS]" << std::endl
545  << std::endl
546  << "Options: "
547  << " --help : print these options" << std::endl
548  << " --print-test-name-list : print the list of names of tests available" << std::endl
549  << " --list : an alias for --print-test-name-list" << std::endl
550  << " --print-test-type-list : print the list of types of tests available" << std::endl
551  << " --print-temp-dir : Print name of temporary directory before running the tests" << std::endl
552  << " --test-type=TYPE : Process only tests of type TYPE" << std::endl
553  << " --test-name=NAME : Process only test whose name matches NAME" << std::endl
554  << " --suite=NAME : an alias (here for compatibility reasons only) "
555  << "for --test-name=NAME" << std::endl
556  << " --assert-on-failure : when a test fails, crash immediately (useful" << std::endl
557  << " when running under a debugger" << std::endl
558  << " --stop-on-failure : when a test fails, stop immediately" << std::endl
559  << " --verbose : Print details of test execution" << std::endl
560  << " --xml : format test run output as xml" << std::endl
561  << " --tempdir=DIR : set temp dir for tests to store output files" << std::endl
562  << " --datadir=DIR : set data dir for tests to read reference files" << std::endl
563  << " --out=FILE : send test result to FILE instead of standard "
564  << "output" << std::endl
565  << " --append=FILE : append test result to FILE instead of standard "
566  << "output" << std::endl
567  ;
568 }
569 
570 void
571 TestRunnerImpl::PrintTestNameList (std::list<TestCase *>::const_iterator begin,
572  std::list<TestCase *>::const_iterator end) const
573 {
574  for (std::list<TestCase *>::const_iterator i = begin; i != end; ++i)
575  {
576  TestCase *test = *i;
577  std::cout << test->GetName () << std::endl;
578  }
579 }
580 
581 void
583 {
584  std::cout << " bvt: Build Verification Tests (to see if build completed successfully)" << std::endl;
585  std::cout << " core: Run all TestSuite-based tests (exclude examples)" << std::endl;
586  std::cout << " example: Examples (to see if example programs run successfully)" << std::endl;
587  std::cout << " performance: Performance Tests (check to see if the system is as fast as expected)" << std::endl;
588  std::cout << " system: System Tests (spans modules to check integration of modules)" << std::endl;
589  std::cout << " unit: Unit Tests (within modules to check basic functionality)" << std::endl;
590 }
591 
592 
593 std::list<TestCase *>
594 TestRunnerImpl::FilterTests (std::string testName, enum TestSuite::Type testType) const
595 {
596  std::list<TestCase *> tests;
597  for (uint32_t i = 0; i < m_suites.size (); ++i)
598  {
599  TestSuite *test = m_suites[i];
600  if (testType != TestSuite::ALL && test->GetTestType () != testType)
601  {
602  // skip test
603  continue;
604  }
605  if (testName != "" && test->GetName () != testName)
606  {
607  // skip test
608  continue;
609  }
610  tests.push_back (test);
611  }
612  return tests;
613 }
614 
615 
616 int
617 TestRunnerImpl::Run (int argc, char *argv[])
618 {
619  std::string testName = "";
620  std::string testTypeString = "";
621  std::string out = "";
622  bool xml = false;
623  bool append = false;
624  bool printTempDir = false;
625  bool printTestTypeList = false;
626  bool printTestNameList = false;
627  char *progname = argv[0];
628 
629  argv++;
630 
631  while (*argv != 0)
632  {
633  char *arg = *argv;
634 
635  if (strcmp(arg, "--assert-on-failure") == 0)
636  {
637  m_assertOnFailure = true;
638  }
639  else if (strcmp (arg, "--stop-on-failure") == 0)
640  {
641  m_continueOnFailure = false;
642  }
643  else if (strcmp (arg, "--verbose") == 0)
644  {
645  m_verbose = true;
646  }
647  else if (strcmp (arg, "--print-temp-dir") == 0)
648  {
649  printTempDir = true;
650  }
651  else if (strcmp (arg, "--update-data") == 0)
652  {
653  m_updateData = true;
654  }
655  else if (strcmp (arg, "--help") == 0)
656  {
657  PrintHelp (progname);
658  return 0;
659  }
660  else if (strcmp (arg, "--print-test-name-list") == 0 ||
661  strcmp(arg, "--list") == 0)
662  {
663  printTestNameList = true;
664  }
665  else if (strcmp (arg, "--print-test-type-list") == 0)
666  {
667  printTestTypeList = true;
668  }
669  else if (strcmp(arg, "--append") == 0)
670  {
671  append = true;
672  }
673  else if (strcmp(arg, "--xml") == 0)
674  {
675  xml = true;
676  }
677  else if (strncmp(arg, "--test-type=", strlen("--test-type=")) == 0)
678  {
679  testTypeString = arg + strlen("--test-type=");
680  }
681  else if (strncmp(arg, "--test-name=", strlen("--test-name=")) == 0)
682  {
683  testName = arg + strlen("--test-name=");
684  }
685  else if (strncmp(arg, "--suite=", strlen("--suite=")) == 0)
686  {
687  testName = arg + strlen("--suite=");
688  }
689  else if (strncmp(arg, "--tempdir=", strlen("--tempdir=")) == 0)
690  {
691  m_tempDir = arg + strlen("--tempdir=");
692  }
693  else if (strncmp(arg, "--out=", strlen("--out=")) == 0)
694  {
695  out = arg + strlen("--out=");
696  }
697  else
698  {
699  // un-recognized command-line argument
700  PrintHelp (progname);
701  return 0;
702  }
703  argv++;
704  }
705  enum TestSuite::Type testType;
706  if (testTypeString == "")
707  {
708  testType = TestSuite::ALL;
709  }
710  else if (testTypeString == "bvt")
711  {
712  testType = TestSuite::BVT;
713  }
714  else if (testTypeString == "core")
715  {
716  testType = TestSuite::ALL;
717  }
718  else if (testTypeString == "example")
719  {
720  testType = TestSuite::EXAMPLE;
721  }
722  else if (testTypeString == "unit")
723  {
724  testType = TestSuite::UNIT;
725  }
726  else if (testTypeString == "system")
727  {
728  testType = TestSuite::SYSTEM;
729  }
730  else
731  {
732  std::cout << "Invalid test type specified: " << testTypeString << std::endl;
734  return 1;
735  }
736 
737  std::list<TestCase *> tests = FilterTests (testName, testType);
738 
739  if (m_tempDir == "")
740  {
742  }
743  if (printTempDir)
744  {
745  std::cout << m_tempDir << std::endl;
746  }
747  if (printTestNameList)
748  {
749  PrintTestNameList (tests.begin (), tests.end ());
750  return 0;
751  }
752  if (printTestTypeList)
753  {
755  return 0;
756  }
757 
758 
759  std::ostream *os;
760  if (out != "")
761  {
762  std::ofstream *ofs;
763  ofs = new std::ofstream();
764  std::ios_base::openmode mode = std::ios_base::out;
765  if (append)
766  {
767  mode |= std::ios_base::app;
768  }
769  else
770  {
771  mode |= std::ios_base::trunc;
772  }
773  ofs->open (out.c_str (), mode);
774  os = ofs;
775  }
776  else
777  {
778  os = &std::cout;
779  }
780 
781  // let's run our tests now.
782  bool failed = false;
783  for (std::list<TestCase *>::const_iterator i = tests.begin (); i != tests.end (); ++i)
784  {
785  TestCase *test = *i;
786  test->Run (this);
787  PrintReport (test, os, xml, 0);
788  if (test->IsFailed ())
789  {
790  failed = true;
791  if (!m_continueOnFailure)
792  {
793  return 1;
794  }
795  }
796  }
797 
798  if (out != "")
799  {
800  delete os;
801  }
802 
803  return failed?1:0;
804 }
805 
806 int
807 TestRunner::Run (int argc, char *argv[])
808 {
809  return TestRunnerImpl::Instance ()->Run (argc, argv);
810 }
811 
812 } // namespace ns3