#ifndef __HAYAI_JUNITXMLOUTPUTTER #define __HAYAI_JUNITXMLOUTPUTTER #include #include #include #include #include #include "hayai_outputter.hpp" namespace hayai { /// JUnit-compatible XML outputter. class JUnitXmlOutputter : public Outputter { private: /// Test case. class TestCase { public: TestCase(const std::string& fixtureName, const std::string& testName, const TestParametersDescriptor& parameters, const TestResult* result) { // Derive a pretty name. std::stringstream nameStream; WriteTestNameToStream(nameStream, fixtureName, testName, parameters); Name = nameStream.str(); // Derive the result. Skipped = !result; if (result) { std::stringstream timeStream; timeStream << std::fixed << std::setprecision(9) << (result->IterationTimeAverage() / 1e9); Time = timeStream.str(); } } std::string Name; std::string Time; bool Skipped; }; /// Test suite map. typedef std::map > TestSuiteMap; public: /// Initialize outputter. /// @param stream Output stream. Must exist for the entire duration of /// the outputter's use. JUnitXmlOutputter(std::ostream& stream) : _stream(stream) { } virtual void Begin(const std::size_t& enabledCount, const std::size_t& disabledCount) { (void)enabledCount; (void)disabledCount; } virtual void End(const std::size_t& executedCount, const std::size_t& disabledCount) { (void)executedCount; (void)disabledCount; // Write the header. _stream << "" << std::endl << "" << std::endl; // Write out each test suite (fixture.) for (TestSuiteMap::iterator testSuiteIt = _testSuites.begin(); testSuiteIt != _testSuites.end(); ++testSuiteIt) { _stream << " first << "\" tests=\"" << testSuiteIt->second.size() << "\">" << std::endl; // Write out each test case. for (std::vector::iterator testCaseIt = testSuiteIt->second.begin(); testCaseIt != testSuiteIt->second.end(); ++testCaseIt) { _stream << " Name); _stream << "\""; if (!testCaseIt->Skipped) _stream << " time=\"" << testCaseIt->Time << "\" />" << std::endl; else { _stream << ">" << std::endl << " " << std::endl << " " << std::endl; } } _stream << " " << std::endl; } _stream << "" << std::endl; } virtual void BeginTest(const std::string& fixtureName, const std::string& testName, const TestParametersDescriptor& parameters, const std::size_t& runsCount, const std::size_t& iterationsCount) { (void)fixtureName; (void)testName; (void)parameters; (void)runsCount; (void)iterationsCount; } virtual void SkipDisabledTest(const std::string& fixtureName, const std::string& testName, const TestParametersDescriptor& parameters, const std::size_t& runsCount, const std::size_t& iterationsCount) { (void)fixtureName; (void)testName; (void)parameters; (void)runsCount; (void)iterationsCount; } virtual void EndTest(const std::string& fixtureName, const std::string& testName, const TestParametersDescriptor& parameters, const TestResult& result) { (void)fixtureName; (void)testName; (void)parameters; TestSuiteMap::iterator fixtureIt = _testSuites.find(fixtureName); if (fixtureIt == _testSuites.end()) { _testSuites[fixtureName] = std::vector(); fixtureIt = _testSuites.find(fixtureName); } std::vector& testCases = fixtureIt->second; testCases.push_back(TestCase(fixtureName, testName, parameters, &result)); /* _stream << JSON_VALUE_SEPARATOR JSON_STRING_BEGIN "runs" JSON_STRING_END JSON_NAME_SEPARATOR JSON_ARRAY_BEGIN; const std::vector& runTimes = result.RunTimes(); for (std::vector::const_iterator it = runTimes.begin(); it != runTimes.end(); ++it) { if (it != runTimes.begin()) _stream << JSON_VALUE_SEPARATOR; _stream << JSON_OBJECT_BEGIN JSON_STRING_BEGIN "duration" JSON_STRING_END JSON_NAME_SEPARATOR << std::fixed << std::setprecision(6) << (double(*it) / 1000000.0) << JSON_OBJECT_END; } _stream << JSON_ARRAY_END; EndTestObject(); */ } private: /// Write an escaped string. /// The escaping is currently very rudimentary and assumes that names, /// parameters etc. are ASCII. /// /// @param str String to write. void WriteEscapedString(const std::string& str) { std::string::const_iterator it = str.begin(); while (it != str.end()) { char c = *it++; switch (c) { case '"': _stream << """; break; case '\'': _stream << "'"; break; case '<': _stream << "<"; break; case '>': _stream << ">"; break; case '&': _stream << "&"; break; default: _stream << c; break; } } } std::ostream& _stream; TestSuiteMap _testSuites; }; } #endif