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