#include <fstream>\r
#include <vector>\r
#include <stdexcept>\r
+#include <memory>\r
\r
namespace tut\r
{\r
*/\r
class cppunit_reporter : public tut::callback\r
{\r
- private:\r
- std::vector<tut::test_result> failed_tests;\r
- std::vector<tut::test_result> passed_tests;\r
- std::string filename;\r
+ std::vector<tut::test_result> failed_tests_;\r
+ std::vector<tut::test_result> passed_tests_;\r
+ const std::string filename_;\r
+ std::auto_ptr<std::ostream> stream_;\r
\r
- std::string encode(const std::string & text)\r
- {\r
- std::string out;\r
\r
- for (unsigned int i=0; i<text.length(); ++i) {\r
- char c = text[i];\r
- switch (c) {\r
- case '<':\r
- out += "<";\r
- break;\r
- case '>':\r
- out += ">";\r
- break;\r
- case '&':\r
- out += "&";\r
- break;\r
- case '\'':\r
- out += "'";\r
- break;\r
- case '"':\r
- out += """;\r
- break;\r
- default:\r
- out += c;\r
- }\r
- }\r
-\r
- return out;\r
- }\r
+ cppunit_reporter(const cppunit_reporter &);\r
+ cppunit_reporter &operator=(const cppunit_reporter &);\r
\r
public:\r
+ explicit cppunit_reporter(const std::string &filename = "testResult.xml")\r
+ : failed_tests_(),\r
+ passed_tests_(),\r
+ filename_(filename),\r
+ stream_(new std::ofstream(filename_.c_str()))\r
+ {\r
+ if (!stream_->good()) {\r
+ throw tut_error("Cannot open output file `" + filename_ + "`");\r
+ }\r
+ }\r
\r
- cppunit_reporter(const std::string & _filename = "")\r
+ explicit cppunit_reporter(std::ostream &stream)\r
+ : failed_tests_(),\r
+ passed_tests_(),\r
+ filename_(),\r
+ stream_(&stream)\r
{\r
- setFilename(_filename);\r
}\r
\r
- void setFilename(const std::string & _filename)\r
+ ~cppunit_reporter()\r
{\r
- if (_filename == "")\r
- {\r
- filename = "testResult.xml";\r
- }\r
- else\r
+ if(filename_.empty())\r
{\r
- filename = _filename;\r
+ stream_.release();\r
}\r
}\r
\r
void run_started()\r
{\r
- failed_tests.clear();\r
- passed_tests.clear();\r
+ failed_tests_.clear();\r
+ passed_tests_.clear();\r
}\r
\r
void test_completed(const tut::test_result& tr)\r
{\r
- if (tr.result == test_result::ok) {\r
- passed_tests.push_back(tr);\r
- } else {\r
- failed_tests.push_back(tr);\r
+ assert(tr.result != test_result::dummy );\r
+ if ( (tr.result == test_result::ok) ||\r
+ (tr.result == test_result::skipped) )\r
+ {\r
+ passed_tests_.push_back(tr);\r
+ }\r
+ else\r
+ {\r
+ failed_tests_.push_back(tr);\r
}\r
}\r
\r
int failures = 0;\r
std::string failure_type;\r
std::string failure_msg;\r
- std::ofstream xmlfile;\r
-\r
- xmlfile.open(filename.c_str(), std::ios::in | std::ios::trunc);\r
- if (!xmlfile.is_open()) {\r
- throw (std::runtime_error("Cannot open file for output"));\r
- }\r
\r
- /* *********************** header ***************************** */\r
- xmlfile << "<?xml version=\"1.0\" encoding='ISO-8859-1' standalone='yes' ?>" << std::endl\r
- << "<TestRun>" << std::endl;\r
+ *stream_ << "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>" << std::endl\r
+ << "<TestRun>" << std::endl;\r
\r
- /* *********************** failed tests ***************************** */\r
- if (failed_tests.size() > 0) {\r
- xmlfile << " <FailedTests>" << std::endl;\r
+ if (failed_tests_.size() > 0)\r
+ {\r
+ *stream_ << " <FailedTests>" << std::endl;\r
\r
- for (unsigned int i=0; i<failed_tests.size(); i++) {\r
- switch (failed_tests[i].result) {\r
+ for (unsigned int i=0; i<failed_tests_.size(); i++)\r
+ {\r
+ switch (failed_tests_[i].result)\r
+ {\r
case test_result::fail:\r
failure_type = "Assertion";\r
failure_msg = "";\r
break;\r
case test_result::ex:\r
failure_type = "Assertion";\r
- failure_msg = "Thrown exception: " + failed_tests[i].exception_typeid + '\n';\r
+ failure_msg = "Thrown exception: " + failed_tests_[i].exception_typeid + '\n';\r
failures++;\r
break;\r
case test_result::warn:\r
failure_type = "Assertion";\r
- failure_msg = "Destructor failed.\n";\r
+ failure_msg = "Destructor failed\n";\r
failures++;\r
break;\r
case test_result::term:\r
failure_type = "Error";\r
- failure_msg = "Test application terminated abnormally.\n";\r
+ failure_msg = "Test application terminated abnormally\n";\r
errors++;\r
break;\r
case test_result::ex_ctor:\r
failure_type = "Error";\r
- failure_msg = "Constructor has thrown an exception: " + failed_tests[i].exception_typeid + '\n';\r
+ failure_msg = "Constructor has thrown an exception: " + failed_tests_[i].exception_typeid + '\n';\r
errors++;\r
break;\r
case test_result::rethrown:\r
failure_type = "Assertion";\r
- failure_msg = "Child failed";\r
+ failure_msg = "Child failed\n";\r
failures++;\r
break;\r
- default:\r
+ default: // ok, skipped, dummy\r
failure_type = "Error";\r
failure_msg = "Unknown test status, this should have never happened. "\r
- "You may just have found a BUG in TUT CppUnit reporter, please report it immediately.\n";\r
+ "You may just have found a bug in TUT, please report it immediately.\n";\r
errors++;\r
break;\r
}\r
\r
- xmlfile << " <FailedTest id=\"" << failed_tests[i].test << "\">" << std::endl\r
- << " <Name>" << encode(failed_tests[i].group) + "::" + encode(failed_tests[i].name) << "</Name>" << std::endl\r
- << " <FailureType>" << failure_type << "</FailureType>" << std::endl\r
- << " <Location>" << std::endl\r
- << " <File>Unknown</File>" << std::endl\r
- << " <Line>Unknown</Line>" << std::endl\r
- << " </Location>" << std::endl\r
- << " <Message>" << encode(failure_msg + failed_tests[i].message) << "</Message>" << std::endl\r
- << " </FailedTest>" << std::endl;\r
+ *stream_ << " <FailedTest id=\"" << failed_tests_[i].test << "\">" << std::endl\r
+ << " <Name>" << encode(failed_tests_[i].group) + "::" + encode(failed_tests_[i].name) << "</Name>" << std::endl\r
+ << " <FailureType>" << failure_type << "</FailureType>" << std::endl\r
+ << " <Location>" << std::endl\r
+ << " <File>Unknown</File>" << std::endl\r
+ << " <Line>Unknown</Line>" << std::endl\r
+ << " </Location>" << std::endl\r
+ << " <Message>" << encode(failure_msg + failed_tests_[i].message) << "</Message>" << std::endl\r
+ << " </FailedTest>" << std::endl;\r
}\r
\r
- xmlfile << " </FailedTests>" << std::endl;\r
+ *stream_ << " </FailedTests>" << std::endl;\r
}\r
\r
/* *********************** passed tests ***************************** */\r
- if (passed_tests.size() > 0) {\r
- xmlfile << " <SuccessfulTests>" << std::endl;\r
-\r
- for (unsigned int i=0; i<passed_tests.size(); i++) {\r
- xmlfile << " <Test id=\"" << passed_tests[i].test << "\">" << std::endl\r
- << " <Name>" << encode(passed_tests[i].group) + "::" + encode(passed_tests[i].name) << "</Name>" << std::endl\r
- << " </Test>" << std::endl;\r
+ if (passed_tests_.size() > 0) {\r
+ *stream_ << " <SuccessfulTests>" << std::endl;\r
+\r
+ for (unsigned int i=0; i<passed_tests_.size(); i++)\r
+ {\r
+ *stream_ << " <Test id=\"" << passed_tests_[i].test << "\">" << std::endl\r
+ << " <Name>" << encode(passed_tests_[i].group) + "::" + encode(passed_tests_[i].name) << "</Name>" << std::endl\r
+ << " </Test>" << std::endl;\r
}\r
\r
- xmlfile << " </SuccessfulTests>" << std::endl;\r
+ *stream_ << " </SuccessfulTests>" << std::endl;\r
}\r
\r
/* *********************** statistics ***************************** */\r
- xmlfile << " <Statistics>" << std::endl\r
- << " <Tests>" << (failed_tests.size() + passed_tests.size()) << "</Tests>" << std::endl\r
- << " <FailuresTotal>" << failed_tests.size() << "</FailuresTotal>" << std::endl\r
- << " <Errors>" << errors << "</Errors>" << std::endl\r
- << " <Failures>" << failures << "</Failures>" << std::endl\r
- << " </Statistics>" << std::endl;\r
+ *stream_ << " <Statistics>" << std::endl\r
+ << " <Tests>" << (failed_tests_.size() + passed_tests_.size()) << "</Tests>" << std::endl\r
+ << " <FailuresTotal>" << failed_tests_.size() << "</FailuresTotal>" << std::endl\r
+ << " <Errors>" << errors << "</Errors>" << std::endl\r
+ << " <Failures>" << failures << "</Failures>" << std::endl\r
+ << " </Statistics>" << std::endl;\r
\r
/* *********************** footer ***************************** */\r
- xmlfile << "</TestRun>" << std::endl;\r
-\r
- xmlfile.close();\r
+ *stream_ << "</TestRun>" << std::endl;\r
}\r
\r
- bool all_ok() const\r
+ virtual bool all_ok() const\r
{\r
- return failed_tests.empty();\r
- };\r
+ return failed_tests_.empty();\r
+ }\r
\r
+ /**\r
+ * \brief Encodes text to XML\r
+ * XML-reserved characters (e.g. "<") are encoded according to specification\r
+ * @param text text to be encoded\r
+ * @return encoded string\r
+ */\r
+ static std::string encode(const std::string & text)\r
+ {\r
+ std::string out;\r
+\r
+ for (unsigned int i=0; i<text.length(); ++i) {\r
+ char c = text[i];\r
+ switch (c) {\r
+ case '<':\r
+ out += "<";\r
+ break;\r
+ case '>':\r
+ out += ">";\r
+ break;\r
+ case '&':\r
+ out += "&";\r
+ break;\r
+ case '\'':\r
+ out += "'";\r
+ break;\r
+ case '"':\r
+ out += """;\r
+ break;\r
+ default:\r
+ out += c;\r
+ }\r
+ }\r
\r
+ return out;\r
+ }\r
};\r
\r
}\r