UTPP
Loading...
Searching...
No Matches
reporter_xml.h
Go to the documentation of this file.
1#pragma once
2/*
3 UTPP - A New Generation of UnitTest++
4 (c) Mircea Neacsu 2017-2024
5
6 See LICENSE file for full copyright information.
7*/
8
13
14#include <iostream>
15#include <iomanip>
16#include <ctime>
17#include <fstream>
18
19#ifdef _WIN32
20#ifndef WIN32_LEAN_AND_MEAN
21#define WIN32_LEAN_AND_MEAN
22#define UTPP_MUST_UNDEF_LEAN_AND_MEAN
23#endif
24
25#ifndef NOMINMAX
26#define NOMINMAX
27#define UTPP_MUST_UNDEF_NOMINMAX
28#endif
29
30#include <windows.h>
31
32#ifdef UTPP_MUST_UNDEF_NOMINMAX
33#undef NOMINMAX
34#endif
35
36#ifdef UTPP_MUST_UNDEF_LEAN_AND_MEAN
37#undef WIN32_LEAN_AND_MEAN
38#endif
39
40#undef UTPP_MUST_UNDEF_LEAN_AND_MEAN
41#undef UTPP_MUST_UNDEF_NOMINMAX
42#endif
43
44namespace UnitTest
45{
46
48class ReporterXml : public ReporterDeferred
49{
50public:
51 explicit ReporterXml (std::ostream& ostream = std::cout);
52
53 int Summary () override;
54 void Clear () override;
55
56protected:
57 void BeginTest (const ReporterDeferred::TestResult& result);
58 void AddFailure (const ReporterDeferred::TestResult& result);
59 void EndTest (const ReporterDeferred::TestResult& result);
60
61private:
62 std::string xml_escape (const std::string& value);
63 std::string build_failure_message (const std::string& file, int line, std::string const& message);
64
65 ReporterXml (ReporterXml const&) = delete;
66 ReporterXml& operator=(ReporterXml const&) = delete;
67
68 std::ostream& os;
69 std::chrono::system_clock::time_point start_time;
70 std::ios orig_state;
71};
72
73inline
74std::string ReporterXml::xml_escape (const std::string& value)
75{
76 //TODO trade style for speed
77 std::string escaped = value;
78 auto replace_char = [&escaped] (char c, const char* repl){
79 for (auto pos = escaped.find(c); pos != std::string::npos; pos = escaped.find(c, pos + 1))
80 escaped.replace(pos, 1, repl);
81
82 };
83 replace_char ('&', "&amp;");
84 replace_char ('<', "&lt;");
85 replace_char ('>', "&gt;");
86 replace_char ('\'', "&apos;");
87 replace_char ('\"', "&quot;");
88
89 return escaped;
90}
91
92inline
93std::string ReporterXml::build_failure_message (const std::string& file, int line, std::string const& message)
94{
95 std::ostringstream failureMessage;
96 failureMessage << file << "(" << line << ") : " << message;
97 return failureMessage.str();
98}
99
105inline
106ReporterXml::ReporterXml (std::ostream& ostream)
107 : os (ostream)
108 , orig_state (nullptr)
109{
110 start_time = std::chrono::system_clock::now();
111 orig_state.copyfmt (os);
112 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
113
114}
115
117inline
119{
120 using namespace std::chrono;
121
122 std::string suite;
123 os.copyfmt (orig_state);
124 auto end_time = time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now ());
125 auto total_time_s = duration_cast<duration<float, std::chrono::seconds::period>>(total_time);
126
127 os << "<utpp-results"
128 << " total=\"" << total_test_count << '\"'
129 << " failed=\"" << total_failed_count << '\"'
130 << " failures=\"" << total_failures_count << '\"' << " duration=\"" << std::fixed << std::setprecision (3)
131#if UTPP_STD_CHRONO_OSTREAM_AVAILABLE
132 << total_time_s << '\"'
133#else
134 << total_time_s.count() << "s\""
135#endif
136 << '>' << std::endl;
137#if defined(__cpp_lib_format)
138 auto start_time_sec = time_point_cast<std::chrono::seconds>(start_time);
139 os << " <start-time>" << std::format("{0:%F} {0:%T}Z", start_time_sec) << "</start-time>" << std::endl;
140#else
141 struct tm* timeinfo;
142 char buffer[80];
143 time_t t = system_clock::to_time_t (start_time);
144 timeinfo = gmtime (&t);
145 strftime (buffer, sizeof(buffer), "%F %TZ", timeinfo);
146 os << " <start-time>" << buffer << "</start-time>" << std::endl;
147#endif
148
149#ifdef _WIN32
150 std::string cmd;
151 std::wstring wcmd{ GetCommandLineW () };
152 int nsz = WideCharToMultiByte (CP_UTF8, 0, wcmd.c_str (), -1, 0, 0, 0, 0);
153 if (nsz)
154 {
155 cmd.resize (nsz);
156 WideCharToMultiByte (CP_UTF8, 0, wcmd.c_str (), -1, &cmd[0], nsz, 0, 0);
157 cmd.resize (nsz - 1); //output is null-terminated
158 }
159 os << " <command-line>" << xml_escape (cmd) << "</command-line>" << std::endl;
160#else
161 std::ifstream cmd_stream("/proc/self/cmdline");
162 if (cmd_stream.good ())
163 {
164 std::string cmd;
165 std::getline(cmd_stream, cmd, '\0');
166 os << " <command-line>" << xml_escape (cmd) << "</command-line>" << std::endl;
167 }
168#endif
169
170 for (auto i = results.cbegin (); i != results.cend (); ++i)
171 {
172 if (i->test_name.empty ()) // New suite flag
173 {
174 if (!suite.empty ())
175 os << " </suite>" << std::endl;
176 suite = i->suite_name;
177 if (suite == DEFAULT_SUITE)
178 os << " <suite";
179 else
180 os << " <suite name=\"" << suite << '\"';
181 if ((i + 1) == results.cend () || (i + 1)->test_name.empty ())
182 {
183 // Next record is another suite. This suite is either empty or disabled
184 os << " /";
185 suite.clear ();
186 }
187 os << '>' << std::endl;
188 }
189 else
190 {
191 BeginTest (*i);
192
193 if (!i->failures.empty ())
194 AddFailure (*i);
195
196 EndTest (*i);
197 }
198 }
199 if (!suite.empty ())
200 os << " </suite>" << std::endl;
201#if defined(__cpp_lib_format)
202 os << " <end-time>" << std::format ("{0:%F} {0:%T}Z", end_time) << "</end-time>" << std::endl;
203#else
204 t = system_clock::to_time_t (end_time);
205 timeinfo = gmtime (&t);
206 strftime (buffer, sizeof (buffer), "%F %TZ", timeinfo);
207 os << " <end-time>" << buffer << "</end-time>" << std::endl;
208#endif
209 os << "</utpp-results>" << std::endl;
211}
212
213inline
215{
216 os.seekp (0);
217 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
218 start_time = std::chrono::system_clock::now();
219
221}
222
223inline
224void ReporterXml::BeginTest (const ReporterDeferred::TestResult& result)
225{
226 os << " <test"
227 << " name=\"" << result.test_name << "\""
228#if UTPP_STD_CHRONO_OSTREAM_AVAILABLE
229 << " time=\"" << result.test_time << "\"";
230#else
231 << " time=\"" << result.test_time.count() << "ms\"";
232#endif
233}
234
235inline
236void ReporterXml::EndTest (const ReporterDeferred::TestResult& result)
237{
238 if (result.failures.empty ())
239 os << "/>";
240 else
241 os << " </test>";
242
243 os << std::endl;
244}
245
246inline
247void ReporterXml::AddFailure (const ReporterDeferred::TestResult& result)
248{
249 os << ">" << std::endl; // close <test> element
250
251 for (auto& fail : result.failures)
252 {
253 std::string escapedMessage = xml_escape (fail.message);
254 std::string message = build_failure_message (fail.filename, fail.line_number, escapedMessage);
255
256 os << " <failure" << " message=\"" << message << "\"" << "/>" << std::endl;
257 }
258}
259
260}
void Clear() override
Reset all statistics.
Definition utpp.h:709
std::deque< TestResult > results
Results of all tests.
Definition utpp.h:352
int total_failures_count
total number of failures
Definition utpp.h:320
std::chrono::milliseconds total_time
total running time in milliseconds
Definition utpp.h:321
int total_test_count
total number of tests
Definition utpp.h:318
virtual int Summary()
Generate results report.
Definition utpp.h:307
int total_failed_count
total number of failed tests
Definition utpp.h:319
int Summary() override
Generate XML report.
Definition reporter_xml.h:118
void Clear() override
Reset all statistics.
Definition reporter_xml.h:214
ReporterXml(std::ostream &ostream=std::cout)
Definition reporter_xml.h:106
Test results including all failure messages
Definition utpp.h:342
std::string test_name
test name
Definition utpp.h:347
std::chrono::milliseconds test_time
test running time in milliseconds
Definition utpp.h:348
#define DEFAULT_SUITE
Name of default suite.
Definition utpp.h:64