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
18namespace UnitTest
19{
20
22class ReporterXml : public ReporterDeferred
23{
24public:
25 explicit ReporterXml (std::ostream& ostream = std::cout);
26
27 int Summary () override;
28 void Clear () override;
29
30protected:
31 void BeginTest (const ReporterDeferred::TestResult& result);
32 void AddFailure (const ReporterDeferred::TestResult& result);
33 void EndTest (const ReporterDeferred::TestResult& result);
34
35private:
36 std::string xml_escape (const std::string& value);
37 std::string build_failure_message (const std::string& file, int line, std::string const& message);
38
39 ReporterXml (ReporterXml const&) = delete;
40 ReporterXml& operator=(ReporterXml const&) = delete;
41
42 std::ostream& os;
43 std::chrono::system_clock::time_point start_time;
44 std::ios orig_state;
45};
46
47inline
48std::string ReporterXml::xml_escape (const std::string& value)
49{
50 //TODO trade style for speed
51 std::string escaped = value;
52 auto replace_char = [&escaped] (char c, const char* repl){
53 for (auto pos = escaped.find(c); pos != std::string::npos; pos = escaped.find(c, pos + 1))
54 escaped.replace(pos, 1, repl);
55
56 };
57 replace_char ('&', "&amp;");
58 replace_char ('<', "&lt;");
59 replace_char ('>', "&gt;");
60 replace_char ('\'', "&apos;");
61 replace_char ('\"', "&quot;");
62
63 return escaped;
64}
65
66inline
67std::string ReporterXml::build_failure_message (const std::string& file, int line, std::string const& message)
68{
69 std::ostringstream failureMessage;
70 failureMessage << file << "(" << line << ") : " << message;
71 return failureMessage.str();
72}
73
79inline
80ReporterXml::ReporterXml (std::ostream& ostream)
81 : os (ostream)
82 , orig_state (nullptr)
83{
84 start_time = std::chrono::system_clock::now();
85 orig_state.copyfmt (os);
86 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
87
88}
89
91inline
93{
94 using namespace std::chrono;
95
96 std::string suite;
97 os.copyfmt (orig_state);
98 auto beg_time = time_point_cast<std::chrono::seconds>(start_time);
99 auto end_time = time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now ());
100 auto total_time_s = duration_cast<duration<float, std::chrono::seconds::period>>(total_time);
101
102 os << "<utpp-results"
103 << " total=\"" << total_test_count << '\"'
104 << " failed=\"" << total_failed_count << '\"'
105 << " failures=\"" << total_failures_count << '\"' << " duration=\"" << std::fixed << std::setprecision (3)
106#if _MSVC_LANG >= 202002L
107 << total_time_s << '\"'
108#else
109 << total_time_s.count() << "s\""
110#endif
111 << '>' << std::endl;
112#if _MSVC_LANG >= 202002L
113 os << " <start-time>" << std::format("{0:%F} {0:%T}Z", beg_time) << "</start-time>" << std::endl;
114#else
115 struct tm* timeinfo;
116 char buffer[80];
117 time_t t = system_clock::to_time_t (start_time);
118 timeinfo = gmtime (&t);
119 strftime (buffer, sizeof(buffer), "%F %TZ", timeinfo);
120 os << " <start-time>" << buffer << "</start-time>" << std::endl;
121#endif
122
123#ifdef _WIN32
124 std::string cmd;
125 std::wstring wcmd{ GetCommandLineW () };
126 int nsz = WideCharToMultiByte (CP_UTF8, 0, wcmd.c_str (), -1, 0, 0, 0, 0);
127 if (nsz)
128 {
129 cmd.resize (nsz);
130 WideCharToMultiByte (CP_UTF8, 0, wcmd.c_str (), -1, &cmd[0], nsz, 0, 0);
131 cmd.resize (nsz - 1); //output is null-terminated
132 }
133 os << " <command-line>" << xml_escape (cmd) << "</command-line>" << std::endl;
134#endif
135
136 for (auto i = results.cbegin (); i != results.cend (); ++i)
137 {
138 if (i->test_name.empty ()) // New suite flag
139 {
140 if (!suite.empty ())
141 os << " </suite>" << std::endl;
142 suite = i->suite_name;
143 if (suite == DEFAULT_SUITE)
144 os << " <suite";
145 else
146 os << " <suite name=\"" << suite << '\"';
147 if ((i + 1) == results.cend () || (i + 1)->test_name.empty ())
148 {
149 // Next record is another suite. This suite is either empty or disabled
150 os << " /";
151 suite.clear ();
152 }
153 os << '>' << std::endl;
154 }
155 else
156 {
157 BeginTest (*i);
158
159 if (!i->failures.empty ())
160 AddFailure (*i);
161
162 EndTest (*i);
163 }
164 }
165 if (!suite.empty ())
166 os << " </suite>" << std::endl;
167#if _MSVC_LANG >= 202002L
168 os << " <end-time>" << std::format ("{0:%F} {0:%T}Z", end_time) << "</end-time>" << std::endl;
169#else
170 t = system_clock::to_time_t (end_time);
171 timeinfo = gmtime (&t);
172 strftime (buffer, sizeof (buffer), "%F %TZ", timeinfo);
173 os << " <end-time>" << buffer << "</end-time>" << std::endl;
174#endif
175 os << "</utpp-results>" << std::endl;
177}
178
179inline
181{
182 os.seekp (0);
183 os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
184 start_time = std::chrono::system_clock::now();
185
187}
188
189inline
190void ReporterXml::BeginTest (const ReporterDeferred::TestResult& result)
191{
192 os << " <test"
193 << " name=\"" << result.test_name << "\""
194#if _MSVC_LANG >= 202002L
195 << " time=\"" << result.test_time << "\"";
196#else
197 << " time=\"" << result.test_time.count() << "ms\"";
198#endif
199}
200
201inline
202void ReporterXml::EndTest (const ReporterDeferred::TestResult& result)
203{
204 if (result.failures.empty ())
205 os << "/>";
206 else
207 os << " </test>";
208
209 os << std::endl;
210}
211
212inline
213void ReporterXml::AddFailure (const ReporterDeferred::TestResult& result)
214{
215 os << ">" << std::endl; // close <test> element
216
217 for (auto& fail : result.failures)
218 {
219 std::string escapedMessage = xml_escape (fail.message);
220 std::string message = build_failure_message (fail.filename, fail.line_number, escapedMessage);
221
222 os << " <failure" << " message=\"" << message << "\"" << "/>" << std::endl;
223 }
224}
225
226}
void Clear() override
Reset all statistics.
Definition utpp.h:711
std::deque< TestResult > results
Results of all tests.
Definition utpp.h:354
int total_failures_count
total number of failures
Definition utpp.h:322
std::chrono::milliseconds total_time
total running time in milliseconds
Definition utpp.h:323
int total_test_count
total number of tests
Definition utpp.h:320
virtual int Summary()
Generate results report.
Definition utpp.h:309
int total_failed_count
total number of failed tests
Definition utpp.h:321
int Summary() override
Generate XML report.
Definition reporter_xml.h:92
void Clear() override
Reset all statistics.
Definition reporter_xml.h:180
ReporterXml(std::ostream &ostream=std::cout)
Definition reporter_xml.h:80
Test results including all failure messages
Definition utpp.h:344
std::string test_name
test name
Definition utpp.h:349
std::chrono::milliseconds test_time
test running time in milliseconds
Definition utpp.h:350
#define DEFAULT_SUITE
Name of default suite.
Definition utpp.h:59