]> git.stg.codes - stg.git/blobdiff - tests/tut/tut_cppunit_reporter.hpp
More std::jthread
[stg.git] / tests / tut / tut_cppunit_reporter.hpp
index 3b9f38c19c96976db6baf6bd8c0700feb1dbd665..62646b09bd03ea17fbfc9a0639624cbfa49d04b0 100644 (file)
@@ -7,6 +7,7 @@
 #include <fstream>\r
 #include <vector>\r
 #include <stdexcept>\r
+#include <memory>\r
 \r
 namespace tut\r
 {\r
@@ -16,72 +17,60 @@ namespace tut
  */\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 += "&lt;";\r
-                        break;\r
-                    case '>':\r
-                        out += "&gt;";\r
-                        break;\r
-                    case '&':\r
-                        out += "&amp;";\r
-                        break;\r
-                    case '\'':\r
-                        out += "&apos;";\r
-                        break;\r
-                    case '"':\r
-                        out += "&quot;";\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
@@ -91,23 +80,18 @@ public:
         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
@@ -115,84 +99,117 @@ public:
                         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 += "&lt;";\r
+                    break;\r
+                case '>':\r
+                    out += "&gt;";\r
+                    break;\r
+                case '&':\r
+                    out += "&amp;";\r
+                    break;\r
+                case '\'':\r
+                    out += "&apos;";\r
+                    break;\r
+                case '"':\r
+                    out += "&quot;";\r
+                    break;\r
+                default:\r
+                    out += c;\r
+            }\r
+        }\r
 \r
+        return out;\r
+    }\r
 };\r
 \r
 }\r