]> git.stg.codes - stg.git/blob - tests/tut/tut_cppunit_reporter.hpp
Merge branch 'master' into full-month-stats
[stg.git] / tests / tut / tut_cppunit_reporter.hpp
1 \r
2 #ifndef TUT_CPPUNIT_REPORTER\r
3 #define TUT_CPPUNIT_REPORTER\r
4 \r
5 #include <tut/tut.hpp>\r
6 #include <string>\r
7 #include <fstream>\r
8 #include <vector>\r
9 #include <stdexcept>\r
10 #include <memory>\r
11 \r
12 namespace tut\r
13 {\r
14 \r
15 /**\r
16  * CppUnit TUT reporter\r
17  */\r
18 class cppunit_reporter : public tut::callback\r
19 {\r
20     std::vector<tut::test_result> failed_tests_;\r
21     std::vector<tut::test_result> passed_tests_;\r
22     const std::string filename_;\r
23     std::auto_ptr<std::ostream> stream_;\r
24 \r
25 \r
26     cppunit_reporter(const cppunit_reporter &);\r
27     cppunit_reporter &operator=(const cppunit_reporter &);\r
28 \r
29 public:\r
30     explicit cppunit_reporter(const std::string &filename = "testResult.xml")\r
31         : failed_tests_(),\r
32           passed_tests_(),\r
33           filename_(filename),\r
34           stream_(new std::ofstream(filename_.c_str()))\r
35     {\r
36         if (!stream_->good()) {\r
37             throw tut_error("Cannot open output file `" + filename_ + "`");\r
38         }\r
39     }\r
40 \r
41     explicit cppunit_reporter(std::ostream &stream)\r
42         : failed_tests_(),\r
43           passed_tests_(),\r
44           filename_(),\r
45           stream_(&stream)\r
46     {\r
47     }\r
48 \r
49     ~cppunit_reporter()\r
50     {\r
51         if(filename_.empty())\r
52         {\r
53             stream_.release();\r
54         }\r
55     }\r
56 \r
57     void run_started()\r
58     {\r
59         failed_tests_.clear();\r
60         passed_tests_.clear();\r
61     }\r
62 \r
63     void test_completed(const tut::test_result& tr)\r
64     {\r
65         assert(tr.result != test_result::dummy );\r
66         if ( (tr.result == test_result::ok) ||\r
67              (tr.result == test_result::skipped) )\r
68         {\r
69             passed_tests_.push_back(tr);\r
70         }\r
71         else\r
72         {\r
73             failed_tests_.push_back(tr);\r
74         }\r
75     }\r
76 \r
77     void run_completed()\r
78     {\r
79         int errors = 0;\r
80         int failures = 0;\r
81         std::string failure_type;\r
82         std::string failure_msg;\r
83 \r
84         *stream_ << "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>" << std::endl\r
85                  << "<TestRun>" << std::endl;\r
86 \r
87         if (failed_tests_.size() > 0)\r
88         {\r
89             *stream_ << "  <FailedTests>" << std::endl;\r
90 \r
91             for (unsigned int i=0; i<failed_tests_.size(); i++)\r
92             {\r
93                 switch (failed_tests_[i].result)\r
94                 {\r
95                     case test_result::fail:\r
96                         failure_type = "Assertion";\r
97                         failure_msg  = "";\r
98                         failures++;\r
99                         break;\r
100                     case test_result::ex:\r
101                         failure_type = "Assertion";\r
102                         failure_msg  = "Thrown exception: " + failed_tests_[i].exception_typeid + '\n';\r
103                         failures++;\r
104                         break;\r
105                     case test_result::warn:\r
106                         failure_type = "Assertion";\r
107                         failure_msg  = "Destructor failed\n";\r
108                         failures++;\r
109                         break;\r
110                     case test_result::term:\r
111                         failure_type = "Error";\r
112                         failure_msg  = "Test application terminated abnormally\n";\r
113                         errors++;\r
114                         break;\r
115                     case test_result::ex_ctor:\r
116                         failure_type = "Error";\r
117                         failure_msg  = "Constructor has thrown an exception: " + failed_tests_[i].exception_typeid + '\n';\r
118                         errors++;\r
119                         break;\r
120                     case test_result::rethrown:\r
121                         failure_type = "Assertion";\r
122                         failure_msg  = "Child failed\n";\r
123                         failures++;\r
124                         break;\r
125                     default: // ok, skipped, dummy\r
126                         failure_type = "Error";\r
127                         failure_msg  = "Unknown test status, this should have never happened. "\r
128                                        "You may just have found a bug in TUT, please report it immediately.\n";\r
129                         errors++;\r
130                         break;\r
131                 }\r
132 \r
133                 *stream_ << "    <FailedTest id=\"" << failed_tests_[i].test << "\">" << std::endl\r
134                             << "      <Name>" << encode(failed_tests_[i].group) + "::" + encode(failed_tests_[i].name) << "</Name>" << std::endl\r
135                             << "      <FailureType>" << failure_type << "</FailureType>" << std::endl\r
136                             << "      <Location>" << std::endl\r
137                             << "        <File>Unknown</File>" << std::endl\r
138                             << "        <Line>Unknown</Line>" << std::endl\r
139                             << "      </Location>" << std::endl\r
140                             << "      <Message>" << encode(failure_msg + failed_tests_[i].message) << "</Message>" << std::endl\r
141                             << "    </FailedTest>" << std::endl;\r
142             }\r
143 \r
144             *stream_ << "  </FailedTests>" << std::endl;\r
145         }\r
146 \r
147         /* *********************** passed tests ***************************** */\r
148         if (passed_tests_.size() > 0) {\r
149             *stream_ << "  <SuccessfulTests>" << std::endl;\r
150 \r
151             for (unsigned int i=0; i<passed_tests_.size(); i++)\r
152             {\r
153                 *stream_ << "    <Test id=\"" << passed_tests_[i].test << "\">" << std::endl\r
154                             << "      <Name>" << encode(passed_tests_[i].group) + "::" + encode(passed_tests_[i].name) << "</Name>" << std::endl\r
155                             << "    </Test>" << std::endl;\r
156             }\r
157 \r
158             *stream_ << "  </SuccessfulTests>" << std::endl;\r
159         }\r
160 \r
161         /* *********************** statistics ***************************** */\r
162         *stream_ << "  <Statistics>" << std::endl\r
163                     << "    <Tests>" << (failed_tests_.size() + passed_tests_.size()) << "</Tests>" << std::endl\r
164                     << "    <FailuresTotal>" << failed_tests_.size() << "</FailuresTotal>" << std::endl\r
165                     << "    <Errors>" << errors << "</Errors>" << std::endl\r
166                     << "    <Failures>" << failures << "</Failures>" << std::endl\r
167                     << "  </Statistics>" << std::endl;\r
168 \r
169         /* *********************** footer ***************************** */\r
170         *stream_ << "</TestRun>" << std::endl;\r
171     }\r
172 \r
173     virtual bool all_ok() const\r
174     {\r
175         return failed_tests_.empty();\r
176     }\r
177 \r
178     /**\r
179      * \brief Encodes text to XML\r
180      * XML-reserved characters (e.g. "<") are encoded according to specification\r
181      * @param text text to be encoded\r
182      * @return encoded string\r
183      */\r
184     static std::string encode(const std::string & text)\r
185     {\r
186         std::string out;\r
187 \r
188         for (unsigned int i=0; i<text.length(); ++i) {\r
189             char c = text[i];\r
190             switch (c) {\r
191                 case '<':\r
192                     out += "&lt;";\r
193                     break;\r
194                 case '>':\r
195                     out += "&gt;";\r
196                     break;\r
197                 case '&':\r
198                     out += "&amp;";\r
199                     break;\r
200                 case '\'':\r
201                     out += "&apos;";\r
202                     break;\r
203                 case '"':\r
204                     out += "&quot;";\r
205                     break;\r
206                 default:\r
207                     out += c;\r
208             }\r
209         }\r
210 \r
211         return out;\r
212     }\r
213 };\r
214 \r
215 }\r
216 \r
217 #endif\r
218 \r