A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
test.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2009 University of Washington
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18#include "test.h"
19
20#include "abort.h"
21#include "assert.h"
22#include "des-metrics.h"
23#include "log.h"
24#include "singleton.h"
25#include "system-path.h"
26
27#include <cmath>
28#include <cstring>
29#include <list>
30#include <map>
31#include <vector>
32
33/**
34 * \file
35 * \ingroup testing
36 * \brief ns3::TestCase, ns3::TestSuite, ns3::TestRunner implementations,
37 */
38
39namespace ns3
40{
41
43
44bool
45TestDoubleIsEqual(const double x1, const double x2, const double epsilon)
46{
47 NS_LOG_FUNCTION(x1 << x2 << epsilon);
48 int exponent;
49 double delta;
50 double difference;
51
52 //
53 // Find exponent of largest absolute value
54 //
55 {
56 double max = (std::fabs(x1) > std::fabs(x2)) ? x1 : x2;
57 std::frexp(max, &exponent);
58 }
59
60 //
61 // Form a neighborhood of size 2 * delta
62 //
63 delta = std::ldexp(epsilon, exponent);
64 difference = x1 - x2;
65
66 return difference <= delta && difference >= -delta;
67}
68
69/**
70 * \ingroup testingimpl
71 * Container for details of a test failure.
72 */
74{
75 /**
76 * Constructor.
77 *
78 * \param [in] _cond The name of the condition being tested.
79 * \param [in] _actual The actual value returned by the test.
80 * \param [in] _limit The expected value.
81 * \param [in] _message The associated message.
82 * \param [in] _file The source file.
83 * \param [in] _line The source line.
84 */
85 TestCaseFailure(std::string _cond,
86 std::string _actual,
87 std::string _limit,
88 std::string _message,
89 std::string _file,
90 int32_t _line);
91 std::string cond; /**< The name of the condition being tested. */
92 std::string actual; /**< The actual value returned by the test. */
93 std::string limit; /**< The expected value. */
94 std::string message; /**< The associated message. */
95 std::string file; /**< The source file. */
96 int32_t line; /**< The source line. */
97};
98
99/**
100 * Output streamer for TestCaseFailure.
101 *
102 * \param [in,out] os The output stream.
103 * \param [in] failure The TestCaseFailure to print.
104 * \returns The stream.
105 */
106std::ostream&
107operator<<(std::ostream& os, const TestCaseFailure& failure)
108{
109 os << " test=\"" << failure.cond << "\" actual=\"" << failure.actual << "\" limit=\""
110 << failure.limit << "\" in=\"" << failure.file << ":" << failure.line << "\" "
111 << failure.message;
112
113 return os;
114}
115
116/**
117 * \ingroup testingimpl
118 * Container for results from a TestCase.
119 */
121{
122 /** Constructor. */
123 Result();
124
125 /** Test running time. */
127 /** TestCaseFailure records for each child. */
128 std::vector<TestCaseFailure> failure;
129 /** \c true if any child TestCases failed. */
131};
132
133/**
134 * \ingroup testingimpl
135 * Container for all tests.
136 * \todo Move TestRunnerImpl to separate file.
137 */
138class TestRunnerImpl : public Singleton<TestRunnerImpl>
139{
140 public:
141 /** Constructor. */
143
144 /**
145 * Add a new top-level TestSuite.
146 * \param [in] testSuite The new TestSuite.
147 */
148 void AddTestSuite(TestSuite* testSuite);
149 /** \copydoc TestCase::MustAssertOnFailure() */
150 bool MustAssertOnFailure() const;
151 /** \copydoc TestCase::MustContinueOnFailure() */
152 bool MustContinueOnFailure() const;
153 /**
154 * Check if this run should update the reference data.
155 * \return \c true if we should update the reference data.
156 */
157 bool MustUpdateData() const;
158 /**
159 * Get the path to the root of the source tree.
160 *
161 * The root directory is defined by the presence of two files:
162 * "VERSION" and "LICENSE".
163 *
164 * \returns The path to the root.
165 */
166 std::string GetTopLevelSourceDir() const;
167 /**
168 * Get the path to temporary directory.
169 * \return The temporary directory path.
170 */
171 std::string GetTempDir() const;
172 /** \copydoc TestRunner::Run() */
173 int Run(int argc, char* argv[]);
174
175 private:
176 /**
177 * Check if this is the root of the source tree.
178 * \param [in] path The path to test.
179 * \returns \c true if \pname{path} is the root.
180 */
181 bool IsTopLevelSourceDir(std::string path) const;
182 /**
183 * Clean up characters not allowed in XML.
184 *
185 * XML files have restrictions on certain characters that may be present in
186 * data. We need to replace these characters with their alternate
187 * representation on the way into the XML file.
188 *
189 * Specifically, we make these replacements:
190 * Raw Source | Replacement
191 * :--------: | :---------:
192 * '<' | "&lt;"
193 * '>' | "&gt;"
194 * '&' | "&amp;"
195 * '"' | "&39;"
196 * '\' | "&quot;"
197 *
198 * \param [in] xml The raw string.
199 * \returns The sanitized string.
200 */
201 std::string ReplaceXmlSpecialCharacters(std::string xml) const;
202 /**
203 * Print the test report.
204 *
205 * \param [in] test The TestCase to print.
206 * \param [in,out] os The output stream.
207 * \param [in] xml Generate XML output if \c true.
208 * \param [in] level Indentation level.
209 */
210 void PrintReport(TestCase* test, std::ostream* os, bool xml, int level);
211 /**
212 * Print the list of all requested test suites.
213 *
214 * \param [in] begin Iterator to the first TestCase to print.
215 * \param [in] end Iterator to the end of the list.
216 * \param [in] printTestType Prepend the test type label if \c true.
217 */
218 void PrintTestNameList(std::list<TestCase*>::const_iterator begin,
219 std::list<TestCase*>::const_iterator end,
220 bool printTestType) const;
221 /** Print the list of test types. */
222 void PrintTestTypeList() const;
223 /**
224 * Print the help text.
225 * \param [in] programName The name of the invoking program.
226 */
227 void PrintHelp(const char* programName) const;
228 /**
229 * Generate the list of tests matching the constraints.
230 *
231 * Test name and type constraints are or'ed. The duration constraint
232 * is and'ed.
233 *
234 * \param [in] testName Include a specific test by name.
235 * \param [in] testType Include all tests of give type.
236 * \param [in] maximumTestDuration Restrict to tests shorter than this.
237 * \returns The list of tests matching the filter constraints.
238 */
239 std::list<TestCase*> FilterTests(std::string testName,
240 TestSuite::Type testType,
241 TestCase::Duration maximumTestDuration);
242
243 /** Container type for the test. */
244 typedef std::vector<TestSuite*> TestSuiteVector;
245
246 TestSuiteVector m_suites; //!< The list of tests.
247 std::string m_tempDir; //!< The temporary directory.
248 bool m_verbose; //!< Produce verbose output.
249 bool m_assertOnFailure; //!< \c true if we should assert on failure.
250 bool m_continueOnFailure; //!< \c true if we should continue on failure.
251 bool m_updateData; //!< \c true if we should update reference data.
252};
253
255 std::string _actual,
256 std::string _limit,
257 std::string _message,
258 std::string _file,
259 int32_t _line)
260 : cond(_cond),
261 actual(_actual),
262 limit(_limit),
263 message(_message),
264 file(_file),
265 line(_line)
266{
267 NS_LOG_FUNCTION(this << _cond << _actual << _limit << _message << _file << _line);
268}
269
271 : childrenFailed(false)
272{
273 NS_LOG_FUNCTION(this);
274}
275
276TestCase::TestCase(std::string name)
277 : m_parent(nullptr),
278 m_dataDir(""),
279 m_runner(nullptr),
280 m_result(nullptr),
281 m_name(name),
283{
284 NS_LOG_FUNCTION(this << name);
285}
286
288{
289 NS_LOG_FUNCTION(this);
290 NS_ASSERT(m_runner == nullptr);
291 m_parent = nullptr;
292 delete m_result;
293 for (auto i = m_children.begin(); i != m_children.end(); ++i)
294 {
295 delete *i;
296 }
297 m_children.clear();
298}
299
300void
302{
303 NS_LOG_FUNCTION(&testCase << duration);
304
305 // Test names are used to create temporary directories,
306 // so we test for illegal characters.
307 //
308 // Windows: <>:"/\|?*
309 // http://msdn.microsoft.com/en-us/library/aa365247(v=vs.85).aspx
310 // Mac: : (deprecated, was path separator in Mac OS Classic, pre X)
311 // Unix: / (and .. may give trouble?)
312 //
313 // The Windows list is too restrictive: we like to label
314 // tests with "val = v1 * v2" or "v1 < 3" or "case: foo --> bar"
315 // So we allow ':<>*"
316
317 std::string badchars = "\"/\\|?";
318 // Badchar Class Regex Count of failing test names
319 // All ":<>\"/\\|?*" 611
320 // Allow ':' "<>\"/\\|?*" 128
321 // Allow ':<>' "\"/\\|?*" 12
322 // Allow ':<>*' "\"/\\|?" 0
323
324 std::string::size_type badch = testCase->m_name.find_first_of(badchars);
325 if (badch != std::string::npos)
326 {
327 /*
328 To count the bad test names, use NS_LOG_UNCOND instead
329 of NS_FATAL_ERROR, and the command
330 $ ./ns3 run "test-runner --list" 2>&1 | grep "^Invalid" | wc
331 */
332 NS_LOG_UNCOND("Invalid test name: cannot contain any of '" << badchars
333 << "': " << testCase->m_name);
334 }
335
336 testCase->m_duration = duration;
337 testCase->m_parent = this;
338 m_children.push_back(testCase);
339}
340
341bool
343{
344 NS_LOG_FUNCTION(this);
345 return m_result->childrenFailed || !m_result->failure.empty();
346}
347
348void
350{
351 NS_LOG_FUNCTION(this << runner);
352 m_result = new Result();
353 m_runner = runner;
354 DoSetup();
356 for (auto i = m_children.begin(); i != m_children.end(); ++i)
357 {
358 TestCase* test = *i;
359 test->Run(runner);
360 if (IsFailed())
361 {
362 goto out;
363 }
364 }
365 DoRun();
366out:
367 m_result->clock.End();
368 DoTeardown();
369 m_runner = nullptr;
370}
371
372std::string
374{
375 NS_LOG_FUNCTION(this);
376 return m_name;
377}
378
381{
382 return m_parent;
383}
384
385void
387 std::string actual,
388 std::string limit,
389 std::string message,
390 std::string file,
391 int32_t line)
392{
393 NS_LOG_FUNCTION(this << cond << actual << limit << message << file << line);
394 m_result->failure.emplace_back(cond, actual, limit, message, file, line);
395 // set childrenFailed flag on parents.
396 TestCase* current = m_parent;
397 while (current != nullptr)
398 {
399 current->m_result->childrenFailed = true;
400 current = current->m_parent;
401 }
402}
403
404bool
406{
407 NS_LOG_FUNCTION(this);
409}
410
411bool
413{
414 NS_LOG_FUNCTION(this);
416}
417
418std::string
420{
421 NS_LOG_FUNCTION(this << filename);
422 const TestCase* current = this;
423 while (current != nullptr && current->m_dataDir.empty())
424 {
425 current = current->m_parent;
426 }
427 if (current == nullptr)
428 {
429 NS_FATAL_ERROR("No one called SetDataDir prior to calling this function");
430 }
431
432 std::string a = SystemPath::Append(m_runner->GetTopLevelSourceDir(), current->m_dataDir);
433 std::string b = SystemPath::Append(a, filename);
434 return b;
435}
436
437std::string
439{
440 NS_LOG_FUNCTION(this << filename);
442 {
443 return CreateDataDirFilename(filename);
444 }
445 else
446 {
447 std::list<std::string> names;
448 const TestCase* current = this;
449 while (current != nullptr)
450 {
451 names.push_front(current->m_name);
452 current = current->m_parent;
453 }
454 std::string tempDir = SystemPath::Append(m_runner->GetTempDir(),
455 SystemPath::Join(names.begin(), names.end()));
456 tempDir = SystemPath::CreateValidSystemPath(tempDir);
457
459 return SystemPath::Append(tempDir, filename);
460 }
461}
462
463bool
465{
466 NS_LOG_FUNCTION(this);
467 return !IsStatusSuccess();
468}
469
470bool
472{
473 NS_LOG_FUNCTION(this);
474 return m_result->failure.empty();
475}
476
477void
478TestCase::SetDataDir(std::string directory)
479{
480 NS_LOG_FUNCTION(this << directory);
481 m_dataDir = directory;
482}
483
484void
486{
487 NS_LOG_FUNCTION(this);
488}
489
490void
492{
493 NS_LOG_FUNCTION(this);
494}
495
497 : TestCase(name),
498 m_type(type)
499{
500 NS_LOG_FUNCTION(this << name << type);
502}
503
506{
507 NS_LOG_FUNCTION(this);
508 return m_type;
509}
510
511void
513{
514 NS_LOG_FUNCTION(this);
515}
516
518 : m_tempDir(""),
519 m_assertOnFailure(false),
520 m_continueOnFailure(true),
521 m_updateData(false)
522{
523 NS_LOG_FUNCTION(this);
524}
525
526void
528{
529 NS_LOG_FUNCTION(this << testSuite);
530 m_suites.push_back(testSuite);
531}
532
533bool
535{
536 NS_LOG_FUNCTION(this);
537 return m_assertOnFailure;
538}
539
540bool
542{
543 NS_LOG_FUNCTION(this);
544 return m_continueOnFailure;
545}
546
547bool
549{
550 NS_LOG_FUNCTION(this);
551 return m_updateData;
552}
553
554std::string
556{
557 NS_LOG_FUNCTION(this);
558 return m_tempDir;
559}
560
561bool
563{
564 NS_LOG_FUNCTION(this << path);
565 bool haveVersion = false;
566 bool haveLicense = false;
567
568 //
569 // If there's a file named VERSION and a file named LICENSE in this
570 // directory, we assume it's our top level source directory.
571 //
572
573 std::list<std::string> files = SystemPath::ReadFiles(path);
574 for (auto i = files.begin(); i != files.end(); ++i)
575 {
576 if (*i == "VERSION")
577 {
578 haveVersion = true;
579 }
580 else if (*i == "LICENSE")
581 {
582 haveLicense = true;
583 }
584 }
585
586 return haveVersion && haveLicense;
587}
588
589std::string
591{
592 NS_LOG_FUNCTION(this);
593 std::string self = SystemPath::FindSelfDirectory();
594 std::list<std::string> elements = SystemPath::Split(self);
595 while (!elements.empty())
596 {
597 std::string path = SystemPath::Join(elements.begin(), elements.end());
598 if (IsTopLevelSourceDir(path))
599 {
600 return path;
601 }
602 elements.pop_back();
603 }
604 NS_FATAL_ERROR("Could not find source directory from self=" << self);
605 return self;
606}
607
608//
609// XML files have restrictions on certain characters that may be present in
610// data. We need to replace these characters with their alternate
611// representation on the way into the XML file.
612//
613std::string
615{
616 NS_LOG_FUNCTION(this << xml);
617 typedef std::map<char, std::string> specials_map;
618 specials_map specials;
619 specials['<'] = "&lt;";
620 specials['>'] = "&gt;";
621 specials['&'] = "&amp;";
622 specials['"'] = "&#39;";
623 specials['\''] = "&quot;";
624
625 std::string result;
626 std::size_t length = xml.length();
627
628 for (size_t i = 0; i < length; ++i)
629 {
630 char character = xml[i];
631
632 auto it = specials.find(character);
633
634 if (it == specials.end())
635 {
636 result.push_back(character);
637 }
638 else
639 {
640 result += it->second;
641 }
642 }
643 return result;
644}
645
646/** Helper to indent output a specified number of steps. */
647struct Indent
648{
649 /**
650 * Constructor.
651 * \param [in] level The number of steps. A step is " ".
652 */
653 Indent(int level);
654 /** The number of steps. */
655 int level;
656};
657
658Indent::Indent(int _level)
659 : level(_level)
660{
661 NS_LOG_FUNCTION(this << _level);
662}
663
664/**
665 * Output streamer for Indent.
666 * \param [in,out] os The output stream.
667 * \param [in] val The Indent object.
668 * \returns The stream.
669 */
670std::ostream&
671operator<<(std::ostream& os, const Indent& val)
672{
673 for (int i = 0; i < val.level; i++)
674 {
675 os << " ";
676 }
677 return os;
678}
679
680void
681TestRunnerImpl::PrintReport(TestCase* test, std::ostream* os, bool xml, int level)
682{
683 NS_LOG_FUNCTION(this << test << os << xml << level);
684 if (test->m_result == nullptr)
685 {
686 // Do not print reports for tests that were not run.
687 return;
688 }
689 // Report times in seconds, from ms timer
690 const double MS_PER_SEC = 1000.;
691 double real = test->m_result->clock.GetElapsedReal() / MS_PER_SEC;
692 double user = test->m_result->clock.GetElapsedUser() / MS_PER_SEC;
693 double system = test->m_result->clock.GetElapsedSystem() / MS_PER_SEC;
694
695 std::streamsize oldPrecision = (*os).precision(3);
696 *os << std::fixed;
697
698 std::string statusString = test->IsFailed() ? "FAIL" : "PASS";
699 if (xml)
700 {
701 *os << Indent(level) << "<Test>" << std::endl;
702 *os << Indent(level + 1) << "<Name>" << ReplaceXmlSpecialCharacters(test->m_name)
703 << "</Name>" << std::endl;
704 *os << Indent(level + 1) << "<Result>" << statusString << "</Result>" << std::endl;
705 *os << Indent(level + 1) << "<Time real=\"" << real << "\" user=\"" << user
706 << "\" system=\"" << system << "\"/>" << std::endl;
707 for (uint32_t i = 0; i < test->m_result->failure.size(); i++)
708 {
709 TestCaseFailure failure = test->m_result->failure[i];
710 *os << Indent(level + 2) << "<FailureDetails>" << std::endl
711 << Indent(level + 3) << "<Condition>" << ReplaceXmlSpecialCharacters(failure.cond)
712 << "</Condition>" << std::endl
713 << Indent(level + 3) << "<Actual>" << ReplaceXmlSpecialCharacters(failure.actual)
714 << "</Actual>" << std::endl
715 << Indent(level + 3) << "<Limit>" << ReplaceXmlSpecialCharacters(failure.limit)
716 << "</Limit>" << std::endl
717 << Indent(level + 3) << "<Message>" << ReplaceXmlSpecialCharacters(failure.message)
718 << "</Message>" << std::endl
719 << Indent(level + 3) << "<File>" << ReplaceXmlSpecialCharacters(failure.file)
720 << "</File>" << std::endl
721 << Indent(level + 3) << "<Line>" << failure.line << "</Line>" << std::endl
722 << Indent(level + 2) << "</FailureDetails>" << std::endl;
723 }
724 for (uint32_t i = 0; i < test->m_children.size(); i++)
725 {
726 TestCase* child = test->m_children[i];
727 PrintReport(child, os, xml, level + 1);
728 }
729 *os << Indent(level) << "</Test>" << std::endl;
730 }
731 else
732 {
733 *os << Indent(level) << statusString << " " << test->GetName() << " " << real << " s"
734 << std::endl;
735 if (m_verbose)
736 {
737 for (uint32_t i = 0; i < test->m_result->failure.size(); i++)
738 {
739 *os << Indent(level) << test->m_result->failure[i] << std::endl;
740 }
741 for (uint32_t i = 0; i < test->m_children.size(); i++)
742 {
743 TestCase* child = test->m_children[i];
744 PrintReport(child, os, xml, level + 1);
745 }
746 }
747 }
748
749 (*os).unsetf(std::ios_base::floatfield);
750 (*os).precision(oldPrecision);
751}
752
753void
754TestRunnerImpl::PrintHelp(const char* program_name) const
755{
756 NS_LOG_FUNCTION(this << program_name);
757 std::cout
758 << "Usage: " << program_name << " [OPTIONS]" << std::endl
759 << std::endl
760 << "Options: " << std::endl
761 << " --help : print these options" << std::endl
762 << " --print-test-name-list : print the list of names of tests available" << std::endl
763 << " --list : an alias for --print-test-name-list" << std::endl
764 << " --print-test-types : print the type of tests along with their names" << std::endl
765 << " --print-test-type-list : print the list of types of tests available" << std::endl
766 << " --print-temp-dir : print name of temporary directory before running "
767 << std::endl
768 << " the tests" << std::endl
769 << " --test-type=TYPE : process only tests of type TYPE" << std::endl
770 << " --test-name=NAME : process only test whose name matches NAME" << std::endl
771 << " --suite=NAME : an alias (here for compatibility reasons only) " << std::endl
772 << " for --test-name=NAME" << std::endl
773 << " --assert-on-failure : when a test fails, crash immediately (useful" << std::endl
774 << " when running under a debugger" << std::endl
775 << " --stop-on-failure : when a test fails, stop immediately" << std::endl
776 << " --fullness=FULLNESS : choose the duration of tests to run: QUICK, " << std::endl
777 << " EXTENSIVE, or TAKES_FOREVER, where EXTENSIVE " << std::endl
778 << " includes QUICK and TAKES_FOREVER includes " << std::endl
779 << " QUICK and EXTENSIVE (only QUICK tests are " << std::endl
780 << " run by default)" << std::endl
781 << " --verbose : print details of test execution" << std::endl
782 << " --xml : format test run output as xml" << std::endl
783 << " --tempdir=DIR : set temp dir for tests to store output files" << std::endl
784 << " --datadir=DIR : set data dir for tests to read reference files" << std::endl
785 << " --out=FILE : send test result to FILE instead of standard "
786 << "output" << std::endl
787 << " --append=FILE : append test result to FILE instead of standard "
788 << "output" << std::endl;
789}
790
791void
792TestRunnerImpl::PrintTestNameList(std::list<TestCase*>::const_iterator begin,
793 std::list<TestCase*>::const_iterator end,
794 bool printTestType) const
795{
796 NS_LOG_FUNCTION(this << &begin << &end << printTestType);
797 std::map<TestSuite::Type, std::string> label;
798
799 label[TestSuite::Type::ALL] = "all ";
800 label[TestSuite::Type::UNIT] = "unit ";
801 label[TestSuite::Type::SYSTEM] = "system ";
802 label[TestSuite::Type::EXAMPLE] = "example ";
803 label[TestSuite::Type::PERFORMANCE] = "performance ";
804
805 for (auto i = begin; i != end; ++i)
806 {
807 auto test = dynamic_cast<TestSuite*>(*i);
808 NS_ASSERT(test != nullptr);
809 if (printTestType)
810 {
811 std::cout << label[test->GetTestType()];
812 }
813 std::cout << test->GetName() << std::endl;
814 }
815}
816
817void
819{
820 NS_LOG_FUNCTION(this);
821 std::cout << " core: Run all TestSuite-based tests (exclude examples)" << std::endl;
822 std::cout << " example: Examples (to see if example programs run successfully)"
823 << std::endl;
824 std::cout
825 << " performance: Performance Tests (check to see if the system is as fast as expected)"
826 << std::endl;
827 std::cout << " system: System Tests (spans modules to check integration of modules)"
828 << std::endl;
829 std::cout << " unit: Unit Tests (within modules to check basic functionality)"
830 << std::endl;
831}
832
833std::list<TestCase*>
834TestRunnerImpl::FilterTests(std::string testName,
835 TestSuite::Type testType,
836 TestCase::Duration maximumTestDuration)
837{
838 NS_LOG_FUNCTION(this << testName << testType);
839 std::list<TestCase*> tests;
840 for (uint32_t i = 0; i < m_suites.size(); ++i)
841 {
842 TestSuite* test = m_suites[i];
843 if (testType != TestSuite::Type::ALL && test->GetTestType() != testType)
844 {
845 // skip test
846 continue;
847 }
848 if (!testName.empty() && test->GetName() != testName)
849 {
850 // skip test
851 continue;
852 }
853
854 // Remove any test cases that should be skipped.
855 for (auto j = test->m_children.begin(); j != test->m_children.end();)
856 {
857 TestCase* testCase = *j;
858
859 // If this test case takes longer than the maximum test
860 // duration that should be run, then don't run it.
861 if (testCase->m_duration > maximumTestDuration)
862 {
863 // Free this test case's memory.
864 delete *j;
865
866 // Remove this test case from the test suite.
867 j = test->m_children.erase(j);
868 }
869 else
870 {
871 // Only advance through the vector elements if this test
872 // case wasn't deleted.
873 ++j;
874 }
875 }
876
877 // Add this test suite.
878 tests.push_back(test);
879 }
880 return tests;
881}
882
883int
884TestRunnerImpl::Run(int argc, char* argv[])
885{
886 NS_LOG_FUNCTION(this << argc << argv);
887 std::string testName = "";
888 std::string testTypeString = "";
889 std::string out = "";
890 std::string fullness = "";
891 bool xml = false;
892 bool append = false;
893 bool printTempDir = false;
894 bool printTestTypeList = false;
895 bool printTestNameList = false;
896 bool printTestTypeAndName = false;
897 TestCase::Duration maximumTestDuration = TestCase::Duration::QUICK;
898 char* progname = argv[0];
899
900 char** argi = argv;
901 ++argi;
902
903 while (*argi != nullptr)
904 {
905 std::string arg = *argi;
906
907 if (arg == "--assert-on-failure")
908 {
909 m_assertOnFailure = true;
910 }
911 else if (arg == "--stop-on-failure")
912 {
913 m_continueOnFailure = false;
914 }
915 else if (arg == "--verbose")
916 {
917 m_verbose = true;
918 }
919 else if (arg == "--print-temp-dir")
920 {
921 printTempDir = true;
922 }
923 else if (arg == "--update-data")
924 {
925 m_updateData = true;
926 }
927 else if (arg == "--help")
928 {
929 PrintHelp(progname);
930 return 0;
931 }
932 else if (arg == "--print-test-name-list" || arg == "--list")
933 {
934 printTestNameList = true;
935 }
936 else if (arg == "--print-test-types")
937 {
938 printTestTypeAndName = true;
939 }
940 else if (arg == "--print-test-type-list")
941 {
942 printTestTypeList = true;
943 }
944 else if (arg == "--append")
945 {
946 append = true;
947 }
948 else if (arg == "--xml")
949 {
950 xml = true;
951 }
952 else if (arg.find("--test-type=") != std::string::npos)
953 {
954 testTypeString = arg.substr(arg.find_first_of('=') + 1);
955 }
956 else if (arg.find("--test-name=") != std::string::npos)
957 {
958 testName = arg.substr(arg.find_first_of('=') + 1);
959 }
960 else if (arg.find("--suite=") != std::string::npos)
961 {
962 testName = arg.substr(arg.find_first_of('=') + 1);
963 }
964 else if (arg.find("--tempdir=") != std::string::npos)
965 {
966 m_tempDir = arg.substr(arg.find_first_of('=') + 1);
967 }
968 else if (arg.find("--out=") != std::string::npos)
969 {
970 out = arg.substr(arg.find_first_of('=') + 1);
971 }
972 else if (arg.find("--fullness=") != std::string::npos)
973 {
974 fullness = arg.substr(arg.find_first_of('=') + 1);
975
976 // Set the maximum test length allowed.
977 if (fullness == "QUICK")
978 {
979 maximumTestDuration = TestCase::Duration::QUICK;
980 }
981 else if (fullness == "EXTENSIVE")
982 {
983 maximumTestDuration = TestCase::Duration::EXTENSIVE;
984 }
985 else if (fullness == "TAKES_FOREVER")
986 {
987 maximumTestDuration = TestCase::Duration::TAKES_FOREVER;
988 }
989 else
990 {
991 // Wrong fullness option
992 PrintHelp(progname);
993 return 3;
994 }
995 }
996 else
997 {
998 // un-recognized command-line argument
999 PrintHelp(progname);
1000 return 0;
1001 }
1002 argi++;
1003 }
1004 TestSuite::Type testType;
1005 if (testTypeString.empty())
1006 {
1007 testType = TestSuite::Type::ALL;
1008 }
1009 else if (testTypeString == "core")
1010 {
1011 testType = TestSuite::Type::ALL;
1012 }
1013 else if (testTypeString == "example")
1014 {
1015 testType = TestSuite::Type::EXAMPLE;
1016 }
1017 else if (testTypeString == "unit")
1018 {
1019 testType = TestSuite::Type::UNIT;
1020 }
1021 else if (testTypeString == "system")
1022 {
1023 testType = TestSuite::Type::SYSTEM;
1024 }
1025 else if (testTypeString == "performance")
1026 {
1028 }
1029 else
1030 {
1031 std::cout << "Invalid test type specified: " << testTypeString << std::endl;
1033 return 1;
1034 }
1035
1036 std::list<TestCase*> tests = FilterTests(testName, testType, maximumTestDuration);
1037
1038 if (m_tempDir.empty())
1039 {
1041 }
1042 if (printTempDir)
1043 {
1044 std::cout << m_tempDir << std::endl;
1045 }
1046 if (printTestNameList)
1047 {
1048 PrintTestNameList(tests.begin(), tests.end(), printTestTypeAndName);
1049 return 0;
1050 }
1051 if (printTestTypeList)
1052 {
1054 return 0;
1055 }
1056
1057 std::ostream* os;
1058 if (!out.empty())
1059 {
1060 std::ofstream* ofs;
1061 ofs = new std::ofstream();
1062 std::ios_base::openmode mode = std::ios_base::out;
1063 if (append)
1064 {
1065 mode |= std::ios_base::app;
1066 }
1067 else
1068 {
1069 mode |= std::ios_base::trunc;
1070 }
1071 ofs->open(out, mode);
1072 os = ofs;
1073 }
1074 else
1075 {
1076 os = &std::cout;
1077 }
1078
1079 // let's run our tests now.
1080 bool failed = false;
1081 if (tests.empty())
1082 {
1083 std::cerr << "Error: no tests match the requested string" << std::endl;
1084 return 1;
1085 }
1086 else if (tests.size() > 1)
1087 {
1088 std::cerr << "Error: tests should be launched separately (one at a time)" << std::endl;
1089 return 1;
1090 }
1091
1092 for (auto i = tests.begin(); i != tests.end(); ++i)
1093 {
1094 TestCase* test = *i;
1095
1096#ifdef ENABLE_DES_METRICS
1097 {
1098 /*
1099 Reorganize argv
1100 Since DES Metrics uses argv[0] for the trace file name,
1101 grab the test name and put it in argv[0],
1102 with test-runner as argv[1]
1103 then the rest of the original arguments.
1104 */
1105 std::string testname = test->GetName();
1106 std::string runner = "[" + SystemPath::Split(argv[0]).back() + "]";
1107
1108 std::vector<std::string> desargs;
1109 desargs.push_back(testname);
1110 desargs.push_back(runner);
1111 for (int i = 1; i < argc; ++i)
1112 {
1113 desargs.push_back(argv[i]);
1114 }
1115
1117 }
1118#endif
1119
1120 test->Run(this);
1121 PrintReport(test, os, xml, 0);
1122 if (test->IsFailed())
1123 {
1124 failed = true;
1126 {
1127 return 1;
1128 }
1129 }
1130 }
1131
1132 if (!out.empty())
1133 {
1134 delete os;
1135 }
1136
1137 return failed ? 1 : 0;
1138}
1139
1140int
1141TestRunner::Run(int argc, char* argv[])
1142{
1143 NS_LOG_FUNCTION(argc << argv);
1144 return TestRunnerImpl::Get()->Run(argc, argv);
1145}
1146
1147std::ostream&
1148operator<<(std::ostream& os, TestSuite::Type type)
1149{
1150 switch (type)
1151 {
1153 return os << "ALL";
1155 return os << "UNIT";
1157 return os << "SYSTEM";
1159 return os << "EXAMPLE";
1161 return os << "PERFORMANCE";
1162 };
1163 return os << "UNKNOWN(" << static_cast<uint32_t>(type) << ")";
1164}
1165
1166std::ostream&
1167operator<<(std::ostream& os, TestCase::Duration duration)
1168{
1169 switch (duration)
1170 {
1172 return os << "QUICK";
1174 return os << "EXTENSIVE";
1176 return os << "TAKES_FOREVER";
1177 };
1178 return os << "UNKNOWN(" << static_cast<uint32_t>(duration) << ")";
1179}
1180
1181} // namespace ns3
NS_ABORT_x macro definitions.
NS_ASSERT() and NS_ASSERT_MSG() macro definitions.
void Initialize(std::vector< std::string > args, std::string outDir="")
Open the DesMetrics trace file and print the header.
Definition: des-metrics.cc:42
A template singleton.
Definition: singleton.h:68
static TestRunnerImpl * Get()
Get a pointer to the singleton instance.
Definition: singleton.h:107
Measure elapsed wall clock time in milliseconds.
int64_t End()
Stop measuring the time since Start() was called.
void Start()
Start a measure.
encapsulates test code
Definition: test.h:1061
std::string m_name
TestCase name.
Definition: test.h:1256
bool MustContinueOnFailure() const
Check if this run should continue on failure.
Definition: test.cc:412
bool IsStatusFailure() const
Check if any tests failed.
Definition: test.cc:464
std::string m_dataDir
My data directory.
Definition: test.h:1253
static constexpr auto QUICK
Definition: test.h:1072
std::string CreateDataDirFilename(std::string filename)
Construct the full path to a file in the data directory.
Definition: test.cc:419
TestCase * m_parent
Pointer to my parent TestCase.
Definition: test.h:1251
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:301
Result * m_result
Results data.
Definition: test.h:1255
bool IsStatusSuccess() const
Check if all tests passed.
Definition: test.cc:471
virtual void DoSetup()
Implementation to do any local setup required for this TestCase.
Definition: test.cc:485
virtual ~TestCase()
Destructor.
Definition: test.cc:287
Duration
How long the test takes to execute.
Definition: test.h:1065
@ EXTENSIVE
Medium length test.
@ TAKES_FOREVER
Very long running test.
std::string CreateTempDirFilename(std::string filename)
Construct the full path to a file in a temporary directory.
Definition: test.cc:438
TestRunnerImpl * m_runner
Pointer to the TestRunner.
Definition: test.h:1254
TestCase * GetParent() const
Get the parent of this TestCase.
Definition: test.cc:380
bool MustAssertOnFailure() const
Check if this run should assert on failure.
Definition: test.cc:405
void SetDataDir(std::string directory)
Set the data directory where reference trace files can be found.
Definition: test.cc:478
virtual void DoTeardown()
Implementation to do any local setup required for this TestCase.
Definition: test.cc:491
void Run(TestRunnerImpl *runner)
Actually run this TestCase.
Definition: test.cc:349
TestCase(const TestCase &)=delete
virtual void DoRun()=0
Implementation to actually run this TestCase.
std::string GetName() const
Definition: test.cc:373
Duration m_duration
TestCase duration.
Definition: test.h:1257
void ReportTestFailure(std::string cond, std::string actual, std::string limit, std::string message, std::string file, int32_t line)
Log the failure of this TestCase.
Definition: test.cc:386
bool IsFailed() const
Check if any tests failed.
Definition: test.cc:342
std::vector< TestCase * > m_children
Vector of my children.
Definition: test.h:1252
static int Run(int argc, char *argv[])
Run the requested suite of tests, according to the given command line arguments.
Definition: test.cc:1141
Container for all tests.
Definition: test.cc:139
void PrintHelp(const char *programName) const
Print the help text.
Definition: test.cc:754
bool m_assertOnFailure
true if we should assert on failure.
Definition: test.cc:249
std::string ReplaceXmlSpecialCharacters(std::string xml) const
Clean up characters not allowed in XML.
Definition: test.cc:614
std::list< TestCase * > FilterTests(std::string testName, TestSuite::Type testType, TestCase::Duration maximumTestDuration)
Generate the list of tests matching the constraints.
Definition: test.cc:834
bool MustUpdateData() const
Check if this run should update the reference data.
Definition: test.cc:548
bool IsTopLevelSourceDir(std::string path) const
Check if this is the root of the source tree.
Definition: test.cc:562
bool m_continueOnFailure
true if we should continue on failure.
Definition: test.cc:250
bool m_updateData
true if we should update reference data.
Definition: test.cc:251
std::string m_tempDir
The temporary directory.
Definition: test.cc:247
std::string GetTempDir() const
Get the path to temporary directory.
Definition: test.cc:555
void PrintReport(TestCase *test, std::ostream *os, bool xml, int level)
Print the test report.
Definition: test.cc:681
std::vector< TestSuite * > TestSuiteVector
Container type for the test.
Definition: test.cc:244
bool MustContinueOnFailure() const
Check if this run should continue on failure.
Definition: test.cc:541
int Run(int argc, char *argv[])
Run the requested suite of tests, according to the given command line arguments.
Definition: test.cc:884
bool MustAssertOnFailure() const
Check if this run should assert on failure.
Definition: test.cc:534
TestRunnerImpl()
Constructor.
Definition: test.cc:517
void AddTestSuite(TestSuite *testSuite)
Add a new top-level TestSuite.
Definition: test.cc:527
bool m_verbose
Produce verbose output.
Definition: test.cc:248
TestSuiteVector m_suites
The list of tests.
Definition: test.cc:246
std::string GetTopLevelSourceDir() const
Get the path to the root of the source tree.
Definition: test.cc:590
void PrintTestTypeList() const
Print the list of test types.
Definition: test.cc:818
void PrintTestNameList(std::list< TestCase * >::const_iterator begin, std::list< TestCase * >::const_iterator end, bool printTestType) const
Print the list of all requested test suites.
Definition: test.cc:792
A suite of tests to run.
Definition: test.h:1268
Type
Type of test.
Definition: test.h:1275
@ PERFORMANCE
This test suite implements a Performance Test.
@ EXAMPLE
This test suite implements an Example Test.
@ UNIT
This test suite implements a Unit Test.
@ SYSTEM
This test suite implements a System Test.
TestSuite(std::string name, Type type=Type::UNIT)
Construct a new test suite.
Definition: test.cc:496
TestSuite::Type m_type
Type of this TestSuite.
Definition: test.h:1313
void DoRun() override
Implementation to actually run this TestCase.
Definition: test.cc:512
TestSuite::Type GetTestType()
get the kind of test this test suite implements
Definition: test.cc:505
ns3::DesMetrics declaration.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:179
#define NS_LOG_UNCOND(msg)
Output the requested message unconditionally.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
std::list< std::string > ReadFiles(std::string path)
Get the list of files located in a file system directory.
Definition: system-path.cc:271
std::list< std::string > Split(std::string path)
Split a file system path into directories according to the local path separator.
Definition: system-path.cc:238
void MakeDirectories(std::string path)
Create all the directories leading to path.
Definition: system-path.cc:330
std::string MakeTemporaryDirectoryName()
Get the name of a temporary directory.
Definition: system-path.cc:285
std::string Append(std::string left, std::string right)
Join two file system path elements.
Definition: system-path.cc:220
std::string Join(std::list< std::string >::const_iterator begin, std::list< std::string >::const_iterator end)
Join a list of file system path directories into a single file system path.
Definition: system-path.cc:247
std::string CreateValidSystemPath(const std::string path)
Replace incompatible characters in a path, to get a path compatible with different file systems.
Definition: system-path.cc:392
std::string FindSelfDirectory()
Get the file system path to the current executable.
Definition: system-path.cc:130
bool TestDoubleIsEqual(const double x1, const double x2, const double epsilon)
Compare two double precision floating point numbers and declare them equal if they are within some ep...
Definition: test.cc:45
Debug message logging.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
std::ostream & operator<<(std::ostream &os, const Angles &a)
Definition: angles.cc:159
-ns3 Test suite for the ns3 wrapper script
ns3::Singleton declaration and template implementation.
Helper to indent output a specified number of steps.
Definition: test.cc:648
Indent(int level)
Constructor.
Definition: test.cc:658
int level
The number of steps.
Definition: test.cc:655
Container for results from a TestCase.
Definition: test.cc:121
Result()
Constructor.
Definition: test.cc:270
std::vector< TestCaseFailure > failure
TestCaseFailure records for each child.
Definition: test.cc:128
bool childrenFailed
true if any child TestCases failed.
Definition: test.cc:130
SystemWallClockMs clock
Test running time.
Definition: test.cc:126
Container for details of a test failure.
Definition: test.cc:74
std::string actual
The actual value returned by the test.
Definition: test.cc:92
std::string file
The source file.
Definition: test.cc:95
std::string message
The associated message.
Definition: test.cc:94
int32_t line
The source line.
Definition: test.cc:96
TestCaseFailure(std::string _cond, std::string _actual, std::string _limit, std::string _message, std::string _file, int32_t _line)
Constructor.
Definition: test.cc:254
std::string cond
The name of the condition being tested.
Definition: test.cc:91
std::string limit
The expected value.
Definition: test.cc:93
ns3::SystemPath declarations.
ns3::TestCase, ns3::TestSuite, ns3::TestRunner declarations, and NS_TEST_ASSERT macro definitions.