X-Git-Url: https://git.stg.codes/stg.git/blobdiff_plain/3156083fd0c328d46be22536720ae33e1ab48090..175f44511b8b3cf327e4ed9268420e423229691a:/tests/tut/tut_cppunit_reporter.hpp?ds=sidebyside

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