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