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