• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 19012754211

02 Nov 2025 01:10PM UTC coverage: 90.677% (+0.006%) from 90.671%
19012754211

push

github

web-flow
Merge pull request #5137 from randombit/jack/clang-tidy-includes

Remove various unused includes flagged by clang-tidy misc-include-cleaner

100457 of 110786 relevant lines covered (90.68%)

12189873.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.48
/src/tests/runner/test_xml_reporter.cpp
1
/*
2
* (C) 2022 Jack Lloyd
3
* (C) 2022 René Meusel, Rohde & Schwarz Cybersecurity
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "test_xml_reporter.h"
9

10
#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
11

12
   #include <botan/build.h>
13
   #include <botan/version.h>
14
   #include <botan/internal/loadstor.h>
15
   #include <botan/internal/target_info.h>
16

17
   #if defined(BOTAN_HAS_OS_UTILS)
18
      #include <botan/internal/os_utils.h>
19
   #endif
20

21
   #include <sstream>
22

23
namespace Botan_Tests {
24

25
namespace {
26

27
std::string full_compiler_version_string() {
1✔
28
   #if defined(__VERSION__)
29
   return __VERSION__;
1✔
30

31
   #elif defined(BOTAN_BUILD_COMPILER_IS_MSVC)
32
   // See https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros
33
   //    If the version number of the Microsoft C/C++ compiler is 15.00.20706.01,
34
   //    the _MSC_FULL_VER macro evaluates to 150020706.
35
   constexpr int major = _MSC_FULL_VER / 10000000;
36
   constexpr int minor = (_MSC_FULL_VER % 10000000) / 100000;
37
   constexpr int patch = _MSC_FULL_VER % 100000;
38
   constexpr int build = _MSC_BUILD;
39

40
   std::ostringstream oss;
41

42
   oss << std::setfill('0') << std::setw(2) << major << "." << std::setw(2) << minor << "." << std::setw(5) << patch
43
       << "." << std::setw(2) << build << "\n";
44

45
   return oss.str();
46
   #else
47
   return "unknown";
48
   #endif
49
}
50

51
std::string full_compiler_name_string() {
1✔
52
   #if defined(BOTAN_BUILD_COMPILER_IS_XCODE)
53
   return "xcode";
54
   #elif defined(BOTAN_BUILD_COMPILER_IS_CLANG)
55
   return "clang";
56
   #elif defined(BOTAN_BUILD_COMPILER_IS_CLANGCL)
57
   return "clangcl";
58
   #elif defined(BOTAN_BUILD_COMPILER_IS_GCC)
59
   return "gcc";
1✔
60
   #elif defined(BOTAN_BUILD_COMPILER_IS_MSVC)
61
   return "Microsoft Visual C++";
62
   #else
63
   return "unknown";
64
   #endif
65
}
66

67
/// formats a given time point in ISO 8601 format (with time zone)
68
std::string format(const std::chrono::system_clock::time_point& tp) {
2,901✔
69
   auto seconds_since_epoch = std::chrono::system_clock::to_time_t(tp);
2,901✔
70
   #if defined(BOTAN_HAS_OS_UTILS)
71
   return Botan::OS::format_time(seconds_since_epoch, "%FT%T%z");
5,802✔
72
   #else
73
   return std::to_string(seconds_since_epoch);
74
   #endif
75
}
76

77
std::string format(const std::chrono::nanoseconds& dur) {
1,738✔
78
   const double secs = static_cast<double>(dur.count()) / 1000000000.0;
1,738✔
79

80
   std::ostringstream out;
1,738✔
81
   out.precision(3);
1,738✔
82
   out << std::fixed << secs;
1,738✔
83
   return out.str();
3,476✔
84
}
1,738✔
85

86
}  // namespace
87

88
XmlReporter::XmlReporter(const Test_Options& opts, std::string output_dir) :
1✔
89
      Reporter(opts), m_output_dir(std::move(output_dir)) {
1✔
90
   set_property("architecture", BOTAN_TARGET_ARCH);
2✔
91
   set_property("compiler", full_compiler_name_string());
2✔
92
   set_property("compiler_version", full_compiler_version_string());
2✔
93
   set_property("timestamp", format(std::chrono::system_clock::now()));
2✔
94
   auto custom_props = opts.report_properties();
1✔
95
   for(const auto& prop : custom_props) {
3✔
96
      set_property(prop.first, prop.second);
2✔
97
   }
98
}
1✔
99

100
void XmlReporter::render() const {
1✔
101
   BOTAN_STATE_CHECK(m_outfile.has_value() && m_outfile->good());
1✔
102

103
   render_preamble(m_outfile.value());
1✔
104
   render_testsuites(m_outfile.value());
1✔
105
}
1✔
106

107
std::string XmlReporter::get_unique_output_filename() const {
1✔
108
   const uint64_t ts = Botan_Tests::Test::timestamp();
1✔
109
   std::vector<uint8_t> seed(8);
1✔
110
   Botan::store_be(ts, seed.data());
1✔
111

112
   std::stringstream ss;
1✔
113
   ss << m_output_dir << "/"
1✔
114
      << "Botan-" << Botan::short_version_string() << "-tests-" << Botan::hex_encode(seed, false) << ".xml";
3✔
115

116
   return ss.str();
1✔
117
}
2✔
118

119
void XmlReporter::next_run() {
1✔
120
   if(m_outfile.has_value()) {
1✔
121
      m_outfile.reset();
×
122
   }
123

124
   set_property("current test run", std::to_string(current_test_run()));
2✔
125
   set_property("total test runs", std::to_string(total_test_runs()));
2✔
126
   const auto file = get_unique_output_filename();
1✔
127
   m_outfile = std::ofstream(file, std::ofstream::out | std::ofstream::trunc);
1✔
128

129
   if(!m_outfile->good()) {
1✔
130
      std::stringstream ss;
×
131
      ss << "Failed to open '" << file << "' for writing JUnit report.";
×
132
      throw Botan::System_Error(ss.str());
×
133
   }
×
134
}
1✔
135

136
// == == == == == == == == == == == == == == == == == == == == == == == == == ==
137
// XML Rendering
138
// == == == == == == == == == == == == == == == == == == == == == == == == == ==
139

140
namespace {
141

142
void replace(std::string& str, const std::string& from, const std::string& to) {
29,821✔
143
   if(from.empty()) {
29,821✔
144
      return;
145
   }
146

147
   for(size_t offset = 0, pos = 0; (pos = str.find(from, offset)) != std::string::npos; offset = pos + to.size()) {
29,880✔
148
      str.replace(pos, from.size(), to);
59✔
149
   }
150
}
151

152
std::string escape(std::string str) {
5,411✔
153
   replace(str, "&", "&amp;");
10,822✔
154
   replace(str, "<", "&lt;");
10,822✔
155
   replace(str, ">", "&gt;");
10,822✔
156
   replace(str, "\"", "&quot;");
10,822✔
157
   replace(str, "'", "&apos;");
10,822✔
158
   return str;
5,411✔
159
}
160

161
std::string format_cdata(std::string str) {
2,766✔
162
   // XML CDATA payloads are not evaluated, hence no special character encoding
163
   // is needed.
164
   // Though the termination sequence (i.e. ']]>') must not appear in
165
   // a CDATA payload frame. The only way to escape it is to terminate the CDATA
166
   // sequence and break the payload's termination sequence into the adjacent
167
   // CDATA frames.
168
   //
169
   //   See: https://stackoverflow.com/a/223782
170
   replace(str, "]]>", "]]]><![CDATA[]>");
5,532✔
171
   //            ^^^ -> ^~~~~~~~~~~~~^^
172

173
   // wrap the (escaped) payload into a CDATA frame
174
   std::ostringstream out;
2,766✔
175
   out << "<![CDATA[" << str << "]]>";
2,766✔
176
   return out.str();
5,532✔
177
}
2,766✔
178

179
}  // namespace
180

181
void XmlReporter::render_preamble(std::ostream& out) const {
1✔
182
   out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1✔
183
}
×
184

185
void XmlReporter::render_properties(std::ostream& out) const {
1✔
186
   if(properties().empty()) {
1✔
187
      return;
188
   }
189

190
   out << "<properties>\n";
1✔
191
   for(const auto& prop : properties()) {
12✔
192
      out << "<property"
11✔
193
          << " name=\"" << escape(prop.first) << "\""
22✔
194
          << " value=\"" << escape(prop.second) << "\""
22✔
195
          << " />\n";
33✔
196
   }
197
   out << "</properties>\n";
1✔
198
}
199

200
void XmlReporter::render_testsuites(std::ostream& out) const {
1✔
201
   // render an empty testsuites tag even if no tests were run
202
   out << "<testsuites"
1✔
203
       << " tests=\"" << tests_run() << "\""
1✔
204
       << " failures=\"" << tests_failed() << "\""
1✔
205
       << " time=\"" << format(elapsed_time()) << "\">\n";
2✔
206

207
   // Note: In the JUnit .xsd spec, <properties> appear only in individual
208
   //       test cases. This deviation from the spec allows us to embed
209
   //       specific platform information about this particular test run.
210
   render_properties(out);
1✔
211

212
   for(const auto& suite : testsuites()) {
412✔
213
      render_testsuite(out, suite.second);
411✔
214
   }
215

216
   out << "</testsuites>\n";
1✔
217
}
1✔
218

219
void XmlReporter::render_testsuite(std::ostream& out, const Testsuite& suite) const {
411✔
220
   out << "<testsuite"
411✔
221
       << " name=\"" << escape(suite.name()) << "\""
822✔
222
       << " tests=\"" << suite.tests_run() << "\""
822✔
223
       << " failures=\"" << suite.tests_failed() << "\""
411✔
224
       << " timestamp=\"" << format(suite.timestamp()) << "\"";
1,233✔
225

226
   const auto elapsed = suite.elapsed_time();
411✔
227
   if(elapsed.has_value()) {
411✔
228
      out << " time=\"" << format(elapsed.value()) << "\"";
810✔
229
   }
230

231
   if(suite.results().empty()) {
411✔
232
      out << " />\n";
×
233
   } else {
234
      out << ">\n";
411✔
235

236
      for(const auto& result : suite.results()) {
2,900✔
237
         render_testcase(out, result);
2,489✔
238
      }
239

240
      out << "</testsuite>\n";
411✔
241
   }
242
}
411✔
243

244
void XmlReporter::render_testcase(std::ostream& out, const TestSummary& test) const {
2,489✔
245
   out << "<testcase"
2,489✔
246
       << " name=\"" << escape(test.name()) << "\""
4,978✔
247
       << " assertions=\"" << test.assertions() << "\""
4,978✔
248
       << " timestamp=\"" << format(test.timestamp()) << "\"";
7,467✔
249

250
   if(test.elapsed_time().has_value()) {
2,489✔
251
      out << " time=\"" << format(test.elapsed_time().value()) << "\"";
4,401✔
252
   }
253

254
   if(test.code_location().has_value()) {
2,489✔
255
      out << " file=\"" << escape(test.code_location()->path) << "\""
4,978✔
256
          << " line=\"" << test.code_location()->line << "\"";
4,978✔
257
   }
258

259
   if(test.passed() && test.notes().empty()) {
2,489✔
260
      out << " />\n";
2,363✔
261
   } else {
262
      out << ">\n";
126✔
263
      render_failures_and_stdout(out, test);
126✔
264
      out << "</testcase>\n";
126✔
265
   }
266
}
2,489✔
267

268
void XmlReporter::render_failures_and_stdout(std::ostream& out, const TestSummary& test) const {
126✔
269
   for(const auto& failure : test.failures()) {
126✔
270
      out << "<failure>\n"
×
271
          << format_cdata(failure) << "\n"
×
272
          << "</failure>\n";
×
273
   }
274

275
   // xUnit format does not have a special tag for test notes, hence we
276
   // render it into the freetext 'system-out'
277
   if(!test.notes().empty()) {
126✔
278
      out << "<system-out>\n";
126✔
279
      for(const auto& note : test.notes()) {
2,892✔
280
         out << format_cdata(note) << '\n';
8,298✔
281
      }
282
      out << "</system-out>\n";
126✔
283
   }
284
}
126✔
285

286
}  // namespace Botan_Tests
287

288
#endif  // defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc