ferencd@0: // ferencd@0: // VMime library (http://www.vmime.org) ferencd@0: // Copyright (C) 2002-2013 Vincent Richard ferencd@0: // ferencd@0: // This program is free software; you can redistribute it and/or ferencd@0: // modify it under the terms of the GNU General Public License as ferencd@0: // published by the Free Software Foundation; either version 3 of ferencd@0: // the License, or (at your option) any later version. ferencd@0: // ferencd@0: // This program is distributed in the hope that it will be useful, ferencd@0: // but WITHOUT ANY WARRANTY; without even the implied warranty of ferencd@0: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ferencd@0: // General Public License for more details. ferencd@0: // ferencd@0: // You should have received a copy of the GNU General Public License along ferencd@0: // with this program; if not, write to the Free Software Foundation, Inc., ferencd@0: // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ferencd@0: // ferencd@0: // Linking this library statically or dynamically with other modules is making ferencd@0: // a combined work based on this library. Thus, the terms and conditions of ferencd@0: // the GNU General Public License cover the whole combination. ferencd@0: // ferencd@0: ferencd@0: #include ferencd@0: #include ferencd@0: ferencd@0: #include ferencd@0: #include ferencd@0: #include ferencd@0: #include ferencd@0: ferencd@0: #include ferencd@0: #include ferencd@0: #include ferencd@0: #include ferencd@0: #include ferencd@0: #include ferencd@0: #include ferencd@0: #include ferencd@0: #include ferencd@0: #include ferencd@0: #include ferencd@0: ferencd@0: #include "vmime/vmime.hpp" ferencd@0: #include "vmime/platforms/posix/posixHandler.hpp" ferencd@0: ferencd@0: ferencd@0: class Clock ferencd@0: { ferencd@0: public: ferencd@0: ferencd@0: void reset() ferencd@0: { ferencd@0: struct timezone tz; ferencd@0: ferencd@0: gettimeofday(&m_start, &tz); ferencd@0: } ferencd@0: ferencd@0: double getDuration() const ferencd@0: { ferencd@0: struct timeval tv; ferencd@0: struct timezone tz; ferencd@0: ferencd@0: gettimeofday(&tv, &tz); ferencd@0: ferencd@0: return static_cast (tv.tv_sec - m_start.tv_sec) ferencd@0: + static_cast (tv.tv_usec - m_start.tv_usec) / 1000000.0; ferencd@0: } ferencd@0: ferencd@0: private: ferencd@0: ferencd@0: struct timeval m_start; ferencd@0: }; ferencd@0: ferencd@0: ferencd@0: class XmlTestListener : public CppUnit::TestListener ferencd@0: { ferencd@0: public: ferencd@0: ferencd@0: XmlTestListener() ferencd@0: : m_doc("utf-8"), m_testElt(NULL) ferencd@0: { ferencd@0: m_doc.setRootElement(new CppUnit::XmlElement("TestRun")); ferencd@0: } ferencd@0: ferencd@0: void startTest(CppUnit::Test* test) ferencd@0: { ferencd@0: m_testElt = new CppUnit::XmlElement("Test"); ferencd@0: m_suiteElt.back()->addElement(m_testElt); ferencd@0: ferencd@0: m_testElt->addElement(new CppUnit::XmlElement("Name", test->getName())); ferencd@0: ferencd@0: m_chrono.reset(); ferencd@0: } ferencd@0: ferencd@0: void addFailure(const CppUnit::TestFailure& failure) ferencd@0: { ferencd@0: CppUnit::XmlElement* failElt = new CppUnit::XmlElement("Failure"); ferencd@0: m_testElt->addElement(failElt); ferencd@0: ferencd@0: failElt->addElement(new CppUnit::XmlElement("FailureType", ferencd@0: failure.isError() ? "Error" : "Assertion")); ferencd@0: ferencd@0: if (failure.sourceLine().isValid()) ferencd@0: { ferencd@0: CppUnit::XmlElement* locElt = new CppUnit::XmlElement("Location"); ferencd@0: failElt->addElement(locElt); ferencd@0: ferencd@0: locElt->addElement(new CppUnit::XmlElement("File", failure.sourceLine().fileName())); ferencd@0: locElt->addElement(new CppUnit::XmlElement("Line", failure.sourceLine().lineNumber())); ferencd@0: } ferencd@0: ferencd@0: CppUnit::XmlElement* exElt = new CppUnit::XmlElement("Exception"); ferencd@0: failElt->addElement(exElt); ferencd@0: ferencd@0: exElt->addElement(new CppUnit::XmlElement("Message", failure.thrownException()->what())); ferencd@0: } ferencd@0: ferencd@0: void endTest(CppUnit::Test* /* test */) ferencd@0: { ferencd@0: std::ostringstream ossTime; ferencd@0: ossTime << (m_chrono.getDuration() * 1000.0); ferencd@0: ferencd@0: m_testElt->addElement(new CppUnit::XmlElement("Time", ossTime.str())); ferencd@0: ferencd@0: m_testElt = NULL; ferencd@0: } ferencd@0: ferencd@0: void startSuite(CppUnit::Test* suite) ferencd@0: { ferencd@0: if (suite->getName() == "All Tests") ferencd@0: return; ferencd@0: ferencd@0: CppUnit::XmlElement* suiteElt = new CppUnit::XmlElement("Suite"); ferencd@0: ferencd@0: if (m_suiteElt.size() == 0) ferencd@0: m_doc.rootElement().addElement(suiteElt); ferencd@0: else ferencd@0: m_suiteElt.back()->addElement(suiteElt); ferencd@0: ferencd@0: m_suiteElt.push_back(suiteElt); ferencd@0: ferencd@0: suiteElt->addElement(new CppUnit::XmlElement("Name", suite->getName())); ferencd@0: } ferencd@0: ferencd@0: void endSuite(CppUnit::Test* /* suite */) ferencd@0: { ferencd@0: if (m_suiteElt.size()) ferencd@0: m_suiteElt.pop_back(); ferencd@0: } ferencd@0: ferencd@0: void startTestRun(CppUnit::Test* /* test */, CppUnit::TestResult* /* eventManager */) ferencd@0: { ferencd@0: } ferencd@0: ferencd@0: void endTestRun(CppUnit::Test* /* test */, CppUnit::TestResult* /* eventManager */) ferencd@0: { ferencd@0: } ferencd@0: ferencd@0: void output(std::ostream& os) ferencd@0: { ferencd@0: os << m_doc.toString(); ferencd@0: } ferencd@0: ferencd@0: private: ferencd@0: ferencd@0: Clock m_chrono; ferencd@0: ferencd@0: CppUnit::XmlDocument m_doc; ferencd@0: std::vector m_suiteElt; ferencd@0: CppUnit::XmlElement* m_testElt; ferencd@0: }; ferencd@0: ferencd@0: ferencd@0: ferencd@0: // see testUtils.hpp ferencd@0: ferencd@0: std::vector & getTestModules() ferencd@0: { ferencd@0: static std::vector allModules; ferencd@0: return allModules; ferencd@0: } ferencd@0: ferencd@0: ferencd@0: void registerTestModule(const char* name_) ferencd@0: { ferencd@0: std::vector & testModules = getTestModules(); ferencd@0: std::string name(name_); ferencd@0: ferencd@0: if (std::find(testModules.begin(), testModules.end(), name) == testModules.end()) ferencd@0: testModules.push_back(name); ferencd@0: } ferencd@0: ferencd@0: ferencd@0: const std::string getNormalizedPath(const std::string& path) ferencd@0: { ferencd@0: std::string res = path; ferencd@0: ferencd@0: for (std::size_t i = 0, n = res.length() ; i < n ; ++i) ferencd@0: { ferencd@0: if (res[i] == '\\') ferencd@0: res[i] = '/'; ferencd@0: } ferencd@0: ferencd@0: return res; ferencd@0: } ferencd@0: ferencd@0: ferencd@0: const std::string getFileNameFromPath(const std::string& path) ferencd@0: { ferencd@0: const std::size_t pos = path.find_last_of('/'); ferencd@0: ferencd@0: if (pos == std::string::npos) ferencd@0: return ""; ferencd@0: ferencd@0: return path.substr(pos + 1); ferencd@0: } ferencd@0: ferencd@0: ferencd@0: static char g_moduleNameBuffer[2048]; ferencd@0: ferencd@0: ferencd@0: const char* getTestModuleNameFromSourceFile(const char *path_) ferencd@0: { ferencd@0: static const std::string testRunnerPath(getNormalizedPath(__FILE__)); ferencd@0: static const std::string testRunnerFileName(getFileNameFromPath(testRunnerPath)); ferencd@0: ferencd@0: const std::string path = getNormalizedPath(path_); ferencd@0: ferencd@0: // "/path/to/testRunner.cpp" --> "/path/to/" ferencd@0: const std::string basePath ferencd@0: (testRunnerPath.begin(), testRunnerPath.end() - testRunnerFileName.length()); ferencd@0: ferencd@0: // "/path/to/module/testFile.cpp" --> "module/testFile.cpp" ferencd@0: const std::string testFileName(getFileNameFromPath(path)); ferencd@0: const std::string testPath(path.begin() + basePath.length(), path.end()); ferencd@0: ferencd@0: // "module/testFile.cpp" --> "module" ferencd@0: const std::string moduleName(testPath.substr(0, testPath.length() - testFileName.length() - 1)); ferencd@0: std::copy(moduleName.begin(), moduleName.end(), g_moduleNameBuffer); ferencd@0: g_moduleNameBuffer[moduleName.length()] = 0; ferencd@0: ferencd@0: return g_moduleNameBuffer; ferencd@0: } ferencd@0: ferencd@0: ferencd@0: int main(int argc, char* argv[]) ferencd@0: { ferencd@0: // Parse arguments ferencd@0: bool xmlOutput = false; ferencd@0: ferencd@0: for (int c = 1 ; c < argc ; ++c) ferencd@0: { ferencd@0: const std::string arg = argv[c]; ferencd@0: ferencd@0: if (arg == "--xml") ferencd@0: xmlOutput = true; ferencd@0: } ferencd@0: ferencd@0: // Run the tests ferencd@0: if (xmlOutput) ferencd@0: { ferencd@0: // Get the test suites from the registry and add them to the list of tests to run ferencd@0: CppUnit::TestRunner runner; ferencd@0: ferencd@0: for (unsigned int i = 0 ; i < getTestModules().size() ; ++i) ferencd@0: { ferencd@0: runner.addTest(CppUnit::TestFactoryRegistry:: ferencd@0: getRegistry(getTestModules()[i]).makeTest()); ferencd@0: } ferencd@0: ferencd@0: std::auto_ptr xmlListener(new XmlTestListener); ferencd@0: ferencd@0: CppUnit::TestResult controller; ferencd@0: controller.addListener(xmlListener.get()); ferencd@0: ferencd@0: CppUnit::TestResultCollector result; ferencd@0: controller.addListener(&result); ferencd@0: ferencd@0: runner.run(controller); ferencd@0: ferencd@0: xmlListener->output(std::cout); ferencd@0: ferencd@0: // Return error code 1 if a test failed ferencd@0: return result.wasSuccessful() ? 0 : 1; ferencd@0: } ferencd@0: else ferencd@0: { ferencd@0: // Get the top level suite from the registry ferencd@0: CppUnit::TextUi::TestRunner runner; ferencd@0: runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest()); ferencd@0: ferencd@0: return runner.run() ? 0 : 1; ferencd@0: } ferencd@0: } ferencd@0: