/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Copyright (c) 2009 University of Washington * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "test.h" #include "assert.h" #include "abort.h" #include namespace ns3 { // // XML files have restrictions on certain characters that may be present in // data. We need to replace these characters with their alternate // representation on the way into the XML file. // std::string ReplaceXmlSpecialCharacters (std::string xml) { std::string specials = "<>&\"'"; std::string replacements[] = {"<", ">", "&", "'", """}; std::string result; std::size_t index, length = xml.length (); for (size_t i = 0; i < length; ++i) { char character = xml[i]; if ((index = specials.find (character)) == std::string::npos) { result.push_back (character); } else { result += replacements[index]; } } return result; } bool TestDoubleIsEqual (const double x1, const double x2, const double epsilon) { int exponent; double delta, difference; // // Find exponent of largest absolute value // { double max = (fabs (x1) > fabs (x2)) ? x1 : x2; frexp (max, &exponent); } // // Form a neighborhood of size 2 * delta // delta = ldexp (epsilon, exponent); difference = x1 - x2; if (difference > delta || difference < -delta) { return false; } return true; } TestCase::TestCase (std::string name) : m_name (name), m_verbose (false), m_continueOnFailure (false), m_detailsReported (false), m_basedir ("invalid"), m_ofs (0), m_error (false) { } TestCase::~TestCase () { } void TestCase::ReportStart (void) { DoReportStart (); } void TestCase::ReportCaseSuccess (void) { UpdateErrorStatus (false); DoReportCaseSuccess (); } void TestCase::ReportCaseFailure (void) { UpdateErrorStatus (true); DoReportCaseFailure (); } void TestCase::ReportTestFailure ( std::string cond, std::string actual, std::string limit, std::string message, std::string file, int32_t line) { UpdateErrorStatus (true); DoReportTestFailure (cond, actual, limit, message, file, line); m_detailsReported = true; } void TestCase::ReportEnd (void) { DoReportStart (); } bool TestCase::Run (void) { // // We set up a flag to make sure the user plays by the rules and actually // does something to report the details of an error. // m_detailsReported = false; DoReportStart (); DoSetup (); bool result = DoRun (); UpdateErrorStatus (result); DoTeardown (); if (GetErrorStatus () == false) { DoReportCaseSuccess (); } else { // // It is a programming error to return an error from a test case without // calling ReportTestFailure. Typically this is done automagically when // using the ASSERT or EXPECT macros. If you don't use these, you must // ReportTestFailure on any errors yourself, which will set // m_detailsReported and make us happy. // NS_ASSERT_MSG (m_detailsReported, "The details of a failing test was not reported"); DoReportCaseFailure (); } DoReportEnd (); return GetErrorStatus (); } void TestCase::SetVerbose (bool verbose) { m_verbose = verbose; } void TestCase::SetContinueOnFailure (bool continueOnFailure) { m_continueOnFailure = continueOnFailure; } void TestCase::SetName (std::string name) { m_name = name; } std::string TestCase::GetName (void) { return m_name; } void TestCase::SetBaseDir (std::string basedir) { m_basedir = basedir; } std::string TestCase::GetBaseDir (void) { return m_basedir; } std::string TestCase::GetSourceDir (std::string file) { std::string::size_type relPathBegin = file.find_first_of ("/"); NS_ABORT_MSG_IF (relPathBegin == std::string::npos, "TestCase::GetSrouceDir(): Internal Error"); std::string::size_type relPathEnd = file.find_last_of ("/"); NS_ABORT_MSG_IF (relPathEnd == std::string::npos, "TestCase::GetSrouceDir(): Internal Error"); return GetBaseDir () + file.substr (relPathBegin, relPathEnd + 1 - relPathBegin); } void TestCase::SetStream (std::ofstream *ofs) { m_ofs = ofs; } std::ofstream * TestCase::GetStream (void) { return m_ofs; } void TestCase::UpdateErrorStatus (bool error) { m_error |= error; } void TestCase::SetErrorStatus (bool error) { m_error = error; } bool TestCase::GetErrorStatus (void) { return m_error; } bool TestCase::ContinueOnFailure (void) { return m_continueOnFailure; } void TestCase::DoReportStart (void) { m_clock.Start (); if (m_ofs == 0) { return; } *m_ofs << " " << std::endl; *m_ofs << " " << ReplaceXmlSpecialCharacters (GetName ()) << "" << std::endl; } void TestCase::DoReportCaseSuccess (void) { if (m_ofs == 0) { return; } *m_ofs << " PASS" << std::endl; } void TestCase::DoReportCaseFailure (void) { if (m_ofs == 0) { return; } *m_ofs << " FAIL" << std::endl; } void TestCase::DoReportTestFailure ( std::string cond, std::string actual, std::string limit, std::string message, std::string file, int32_t line) { if (m_ofs == 0) { return; } *m_ofs << " " << std::endl; *m_ofs << " " << ReplaceXmlSpecialCharacters (cond) << "" << std::endl; *m_ofs << " " << ReplaceXmlSpecialCharacters (actual) << "" << std::endl; *m_ofs << " " << ReplaceXmlSpecialCharacters (limit) << "" << std::endl; *m_ofs << " " << ReplaceXmlSpecialCharacters (message) << "" << std::endl; *m_ofs << " " << ReplaceXmlSpecialCharacters (file) << "" << std::endl; *m_ofs << " " << line << "" << std::endl; *m_ofs << " " << std::endl; } void TestCase::DoReportEnd (void) { m_clock.End (); if (m_ofs == 0) { return; } (*m_ofs).precision (2); *m_ofs << std::fixed; *m_ofs << " " << "real " << m_clock.GetElapsedReal () * 1e-3 << " user " << m_clock.GetElapsedUser () * 1e-3 << " system " << m_clock.GetElapsedSystem () * 1e-3 << "" << std::endl; *m_ofs << " " << std::endl; } void TestCase::DoSetup (void) { } void TestCase::DoTeardown (void) { } TestSuite::TestSuite (std::string name, TestType type) : m_name (name), m_verbose (false), m_basedir ("invalid"), m_ofs (0), m_error (false), m_type (type) { TestRunner::AddTestSuite (this); } TestSuite::~TestSuite () { for (TestCaseVector_t::iterator i = m_tests.begin (); i != m_tests.end (); ++i) { delete *i; *i = 0; } m_tests.erase (m_tests.begin (), m_tests.end ()); } void TestSuite::ReportStart (void) { DoReportStart (); } void TestSuite::ReportSuccess (void) { UpdateErrorStatus (false); DoReportSuccess (); } void TestSuite::ReportFailure (void) { UpdateErrorStatus (true); DoReportFailure (); } void TestSuite::ReportEnd (void) { DoReportEnd (); } bool TestSuite::Run (void) { DoReportStart (); DoSetup (); bool result = DoRun (); UpdateErrorStatus (result); DoTeardown (); if (GetErrorStatus () == false) { DoReportSuccess (); } else { DoReportFailure (); } DoReportEnd (); return GetErrorStatus (); } uint32_t TestSuite::AddTestCase (TestCase *testCase) { uint32_t index = m_tests.size (); m_tests.push_back (testCase); return index; } uint32_t TestSuite::GetNTestCases (void) { return m_tests.size (); } TestCase * TestSuite::GetTestCase (uint32_t n) { return m_tests[n]; } TestSuite::TestType TestSuite::GetTestType (void) { return m_type; } void TestSuite::SetVerbose (bool verbose) { m_verbose = verbose; } void TestSuite::SetContinueOnFailure (bool continueOnFailure) { m_continueOnFailure = continueOnFailure; } void TestSuite::SetName (std::string name) { m_name = name; } std::string TestSuite::GetName (void) { return m_name; } void TestSuite::SetBaseDir (std::string basedir) { m_basedir = basedir; } std::string TestSuite::GetBaseDir (void) { return m_basedir; } void TestSuite::SetStream (std::ofstream *ofs) { m_ofs = ofs; } void TestSuite::UpdateErrorStatus (bool error) { m_error |= error; } void TestSuite::SetErrorStatus (bool error) { m_error = error; } bool TestSuite::GetErrorStatus (void) { return m_error; } bool TestSuite::ContinueOnFailure (void) { return m_continueOnFailure; } void TestSuite::DoReportStart (void) { m_clock.Start (); if (m_ofs == 0) { return; } *m_ofs << "" << std::endl; *m_ofs << " " << ReplaceXmlSpecialCharacters (GetName ()) << "" << std::endl; } void TestSuite::DoReportFailure (void) { if (m_ofs == 0) { return; } *m_ofs << " FAIL" << std::endl; } void TestSuite::DoReportSuccess (void) { if (m_ofs == 0) { return; } *m_ofs << " PASS" << std::endl; } void TestSuite::DoReportEnd (void) { m_clock.End (); if (m_ofs == 0) { return; } (*m_ofs).precision (2); *m_ofs << std::fixed; *m_ofs << " " << "real " << m_clock.GetElapsedReal () * 1e-3 << " user " << m_clock.GetElapsedUser () * 1e-3 << " system " << m_clock.GetElapsedSystem () * 1e-3 << "" << std::endl; *m_ofs << "" << std::endl; } void TestSuite::DoSetup (void) { } bool TestSuite::DoRun (void) { SetErrorStatus (false); for (TestCaseVector_t::iterator i = m_tests.begin (); i != m_tests.end (); ++i) { // // Set the current options for the test case // (*i)->SetVerbose (m_verbose); (*i)->SetContinueOnFailure (m_continueOnFailure); (*i)->SetBaseDir (m_basedir); (*i)->SetStream (m_ofs); // // Run the test case // bool result = (*i)->Run (); UpdateErrorStatus (result); // // Exit if we have detected an error and we are not allowing multiple filures // if (GetErrorStatus () && m_continueOnFailure == false) { return GetErrorStatus (); } } return GetErrorStatus (); } void TestSuite::DoTeardown (void) { } class TestRunnerImpl { public: uint32_t AddTestSuite (TestSuite *testSuite); uint32_t GetNTestSuites (void); TestSuite *GetTestSuite (uint32_t n); bool RunTestSuite (uint32_t n); static TestRunnerImpl *Instance (void); private: TestRunnerImpl (); ~TestRunnerImpl (); typedef std::vector TestSuiteVector_t; TestSuiteVector_t m_suites; }; TestRunnerImpl::TestRunnerImpl () { } TestRunnerImpl::~TestRunnerImpl () { } TestRunnerImpl * TestRunnerImpl::Instance (void) { static TestRunnerImpl runner; return &runner; } uint32_t TestRunnerImpl::AddTestSuite (TestSuite *testSuite) { uint32_t index = m_suites.size (); m_suites.push_back (testSuite); return index; } uint32_t TestRunnerImpl::GetNTestSuites (void) { return m_suites.size (); } TestSuite * TestRunnerImpl::GetTestSuite (uint32_t n) { return m_suites[n]; } uint32_t TestRunner::AddTestSuite (TestSuite *testSuite) { return TestRunnerImpl::Instance ()->AddTestSuite (testSuite); } uint32_t TestRunner::GetNTestSuites (void) { return TestRunnerImpl::Instance ()->GetNTestSuites (); } TestSuite * TestRunner::GetTestSuite (uint32_t n) { return TestRunnerImpl::Instance ()->GetTestSuite (n); } } // namespace ns3