mimic++ v2
Loading...
Searching...
No Matches
Reporter.hpp
Go to the documentation of this file.
1// // Copyright Dominic (DNKpp) Koepke 2024 - 2024.
2// // Distributed under the Boost Software License, Version 1.0.
3// // (See accompanying file LICENSE_1_0.txt or copy at
4// // https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef MIMICPP_REPORTER_HPP
7#define MIMICPP_REPORTER_HPP
8
9#pragma once
10
11#include "mimic++/Call.hpp"
12#include "mimic++/Fwd.hpp"
13#include "mimic++/Printer.hpp"
14#include "mimic++/Reports.hpp"
15
16#include <algorithm>
17#include <cassert>
18#include <exception>
19#include <functional>
20#include <memory>
21#include <optional>
22#include <ranges>
23#include <vector>
24
25namespace mimicpp
26{
55 {
56 public:
60 virtual ~IReporter() = default;
61
73 [[noreturn]]
74 virtual void report_no_matches(
75 CallReport call,
76 std::vector<MatchReport> matchReports
77 ) = 0;
78
90 [[noreturn]]
92 CallReport call,
93 std::vector<MatchReport> matchReports
94 ) = 0;
95
103 virtual void report_full_match(
104 CallReport call,
105 MatchReport matchReport
106 ) noexcept = 0;
107
120 ExpectationReport expectationReport
121 ) = 0;
122
133 virtual void report_error(StringT message) = 0;
134
144 CallReport call,
145 ExpectationReport expectationReport,
146 std::exception_ptr exception
147 ) = 0;
148
149 protected:
150 [[nodiscard]]
151 IReporter() = default;
152
153 IReporter(const IReporter&) = default;
154 IReporter& operator =(const IReporter&) = default;
155 IReporter(IReporter&&) = default;
157 };
158
159 template <typename Data = std::nullptr_t>
160 class Error final
161 : public std::runtime_error
162 {
163 public:
164 [[nodiscard]]
165 explicit Error(
166 const std::string& what,
167 Data&& data = Data{},
168 const std::source_location& loc = std::source_location::current()
169 )
170 : std::runtime_error{what},
171 m_Data{std::move(data)},
172 m_Loc{loc}
173 {
174 }
175
176 [[nodiscard]]
177 const Data& data() const noexcept
178 {
179 return m_Data;
180 }
181
182 [[nodiscard]]
183 const std::source_location& where() const noexcept
184 {
185 return m_Loc;
186 }
187
188 private:
189 Data m_Data;
190 std::source_location m_Loc;
191 };
192
199 template <
200 std::invocable<const StringT&> auto successReporter,
201 std::invocable<const StringT&> auto warningReporter,
202 std::invocable<const StringT&> auto failReporter
203 >
205 : public IReporter
206 {
207 public:
208 [[noreturn]]
209 void report_no_matches(const CallReport call, const std::vector<MatchReport> matchReports) override
210 {
211 StringStreamT ss{};
212 ss << "No match for ";
213 print(
214 std::ostreambuf_iterator{ss},
215 call);
216 ss << "\n";
217
218 if (std::ranges::empty(matchReports))
219 {
220 ss << "No expectations available.\n";
221 }
222 else
223 {
224 format::format_to(
225 std::ostreambuf_iterator{ss},
226 "{} available expectation(s):\n",
227 std::ranges::size(matchReports));
228
229 for (const auto& report : matchReports)
230 {
231 print(
232 std::ostreambuf_iterator{ss},
233 report);
234 ss << "\n";
235 }
236 }
237
238 send_fail(std::move(ss).str());
239 }
240
241 [[noreturn]]
242 void report_inapplicable_matches(const CallReport call, const std::vector<MatchReport> matchReports) override
243 {
244 StringStreamT ss{};
245 ss << "No applicable match for ";
246 print(
247 std::ostreambuf_iterator{ss},
248 call);
249 ss << "\n";
250
251 ss << "Tested expectations:\n";
252 for (const auto& report : matchReports)
253 {
254 print(
255 std::ostreambuf_iterator{ss},
256 report);
257 ss << "\n";
258 }
259
260 send_fail(std::move(ss).str());
261 }
262
263 void report_full_match(const CallReport call, const MatchReport matchReport) noexcept override
264 {
265 StringStreamT ss{};
266 ss << "Found match for ";
267 print(
268 std::ostreambuf_iterator{ss},
269 call);
270 ss << "\n";
271
272 print(
273 std::ostreambuf_iterator{ss},
274 matchReport);
275 ss << "\n";
276
277 send_success(std::move(ss).str());
278 }
279
280 void report_unfulfilled_expectation(const ExpectationReport expectationReport) override
281 {
282 if (0 == std::uncaught_exceptions())
283 {
284 StringStreamT ss{};
285 ss << "Unfulfilled expectation:\n";
286 print(
287 std::ostreambuf_iterator{ss},
288 expectationReport);
289 ss << "\n";
290
291 send_fail(std::move(ss).str());
292 }
293 }
294
295 void report_error(const StringT message) override
296 {
297 if (0 == std::uncaught_exceptions())
298 {
299 send_fail(message);
300 }
301 }
302
304 const CallReport call,
305 const ExpectationReport expectationReport,
306 const std::exception_ptr exception
307 ) override
308 {
309 StringStreamT ss{};
310 ss << "Unhandled exception: ";
311
312 try
313 {
314 std::rethrow_exception(exception);
315 }
316 catch (const std::exception& e)
317 {
318 ss << "what: "
319 << e.what()
320 << "\n";
321 }
322 catch (...)
323 {
324 ss << "Unknown exception type.\n";
325 }
326
327 ss << "while checking expectation:\n";
328 print(
329 std::ostreambuf_iterator{ss},
330 expectationReport);
331 ss << "\n";
332
333 ss << "For ";
334 print(
335 std::ostreambuf_iterator{ss},
336 call);
337 ss << "\n";
338
339 send_warning(std::move(ss).str());
340 }
341
342 private:
343 void send_success(const StringT& msg)
344 {
345 std::invoke(successReporter, msg);
346 }
347
348 void send_warning(const StringT& msg)
349 {
350 std::invoke(warningReporter, msg);
351 }
352
353 [[noreturn]]
354 void send_fail(const StringT& msg)
355 {
356 // GCOVR_EXCL_START
357 std::invoke(failReporter, msg);
358 unreachable();
359 // GCOVR_EXCL_STOP
360 }
361 };
362
365
369 class DefaultReporter final
370 : public IReporter
371 {
372 public:
373 [[noreturn]]
375 CallReport call,
376 std::vector<MatchReport> matchReports
377 ) override
378 {
379 assert(
380 std::ranges::all_of(
381 matchReports,
382 std::bind_front(std::equal_to{}, MatchResult::none),
384
385 const std::source_location loc{call.fromLoc};
386 throw UnmatchedCallT{
387 "No match found.",
388 {std::move(call), std::move(matchReports)},
389 loc
390 };
391 }
392
393 [[noreturn]]
395 CallReport call,
396 std::vector<MatchReport> matchReports
397 ) override
398 {
399 assert(
400 std::ranges::all_of(
401 matchReports,
402 std::bind_front(std::equal_to{}, MatchResult::inapplicable),
404
405 const std::source_location loc{call.fromLoc};
406 throw UnmatchedCallT{
407 "No applicable match found.",
408 {std::move(call), std::move(matchReports)},
409 loc
410 };
411 }
412
414 [[maybe_unused]] CallReport call,
415 [[maybe_unused]] MatchReport matchReport
416 ) noexcept override
417 {
418 assert(MatchResult::full == evaluate_match_report(matchReport));
419 }
420
422 ExpectationReport expectationReport
423 ) override
424 {
425 if (0 == std::uncaught_exceptions())
426 {
428 "Expectation is unfulfilled.",
429 std::move(expectationReport)
430 };
431 }
432 }
433
434 void report_error(StringT message) override
435 {
436 if (0 == std::uncaught_exceptions())
437 {
438 throw Error{message};
439 }
440 }
441
443 [[maybe_unused]] CallReport call,
444 [[maybe_unused]] ExpectationReport expectationReport,
445 [[maybe_unused]] std::exception_ptr exception
446 ) override
447 {
448 }
449 };
450
454}
455
456namespace mimicpp::detail
457{
458 [[nodiscard]]
459 inline std::unique_ptr<IReporter>& get_reporter() noexcept
460 {
461 static std::unique_ptr<IReporter> reporter{
462 std::make_unique<DefaultReporter>()
463 };
464 return reporter;
465 }
466
467 [[noreturn]]
468 inline void report_no_matches(
469 CallReport callReport,
470 std::vector<MatchReport> matchReports
471 )
472 {
473 get_reporter()
474 // GCOVR_EXCL_START
475 ->report_no_matches(
476 // GCOVR_EXCL_STOP
477 std::move(callReport),
478 std::move(matchReports));
479
480 // GCOVR_EXCL_START
481 // ReSharper disable once CppUnreachableCode
482 unreachable();
483 // GCOVR_EXCL_STOP
484 }
485
486 [[noreturn]]
487 inline void report_inapplicable_matches(
488 CallReport callReport,
489 std::vector<MatchReport> matchReports
490 )
491 {
492 get_reporter()
493 // GCOVR_EXCL_START
494 ->report_inapplicable_matches(
495 // GCOVR_EXCL_STOP
496 std::move(callReport),
497 std::move(matchReports));
498
499 // GCOVR_EXCL_START
500 // ReSharper disable once CppUnreachableCode
501 unreachable();
502 // GCOVR_EXCL_STOP
503 }
504
505 inline void report_full_match(
506 CallReport callReport,
507 MatchReport matchReport
508 ) noexcept
509 {
510 get_reporter()
511 ->report_full_match(
512 std::move(callReport),
513 std::move(matchReport));
514 }
515
516 inline void report_unfulfilled_expectation(
517 ExpectationReport expectationReport
518 )
519 {
520 get_reporter()
521 ->report_unfulfilled_expectation(std::move(expectationReport));
522 }
523
524 inline void report_error(StringT message)
525 {
526 get_reporter()
527 ->report_error(std::move(message));
528 }
529
530 inline void report_unhandled_exception(
531 CallReport callReport,
532 ExpectationReport expectationReport,
533 const std::exception_ptr& exception
534 )
535 {
536 get_reporter()
537 ->report_unhandled_exception(
538 std::move(callReport),
539 std::move(expectationReport),
540 exception);
541 }
542}
543
544namespace mimicpp
545{
554 template <std::derived_from<IReporter> T, typename... Args>
555 requires std::constructible_from<T, Args...>
556 void install_reporter(Args&&... args) // NOLINT(cppcoreguidelines-missing-std-forward)
557 {
558 detail::get_reporter() = std::make_unique<T>(
559 std::forward<Args>(args)...);
560 }
561
562 namespace detail
563 {
564 template <typename T>
565 class ReporterInstaller
566 {
567 public:
568 template <typename... Args>
569 explicit ReporterInstaller(Args&&... args)
570 {
572 std::forward<Args>(args)...);
573 }
574 };
575 }
576
591}
592
593#endif
A reporter, which creates text messages and reports them via the provided callbacks.
Definition Reporter.hpp:206
void report_error(const StringT message) override
Expects rather unspecific errors.
Definition Reporter.hpp:295
void report_full_match(const CallReport call, const MatchReport matchReport) noexcept override
Expects the report about a full matching expectation.
Definition Reporter.hpp:263
void report_unhandled_exception(const CallReport call, const ExpectationReport expectationReport, const std::exception_ptr exception) override
Expects reports about unhandled exceptions, during handle_call.
Definition Reporter.hpp:303
void report_unfulfilled_expectation(const ExpectationReport expectationReport) override
Expects the report of an unfulfilled expectation.
Definition Reporter.hpp:280
void report_no_matches(const CallReport call, const std::vector< MatchReport > matchReports) override
Expects reports about all none matching expectations. This is only called, if there are no better opt...
Definition Reporter.hpp:209
void report_inapplicable_matches(const CallReport call, const std::vector< MatchReport > matchReports) override
Expects reports about all inapplicable matching expectations. This is only called,...
Definition Reporter.hpp:242
Contains the extracted info from a typed call::Info.
Definition Reports.hpp:205
std::source_location fromLoc
Definition Reports.hpp:219
The default reporter.
Definition Reporter.hpp:371
void report_unhandled_exception(CallReport call, ExpectationReport expectationReport, std::exception_ptr exception) override
Expects reports about unhandled exceptions, during handle_call.
Definition Reporter.hpp:442
void report_no_matches(CallReport call, std::vector< MatchReport > matchReports) override
Expects reports about all none matching expectations. This is only called, if there are no better opt...
Definition Reporter.hpp:374
void report_error(StringT message) override
Expects rather unspecific errors.
Definition Reporter.hpp:434
void report_unfulfilled_expectation(ExpectationReport expectationReport) override
Expects the report of an unfulfilled expectation.
Definition Reporter.hpp:421
void report_full_match(CallReport call, MatchReport matchReport) noexcept override
Expects the report about a full matching expectation.
Definition Reporter.hpp:413
void report_inapplicable_matches(CallReport call, std::vector< MatchReport > matchReports) override
Expects reports about all inapplicable matching expectations. This is only called,...
Definition Reporter.hpp:394
Definition Reporter.hpp:162
Error(const std::string &what, Data &&data=Data{}, const std::source_location &loc=std::source_location::current())
Definition Reporter.hpp:165
const Data & data() const noexcept
Definition Reporter.hpp:177
const std::source_location & where() const noexcept
Definition Reporter.hpp:183
Contains the extracted info from a typed expectation.
Definition Reports.hpp:320
The reporter interface.
Definition Reporter.hpp:55
IReporter(const IReporter &)=default
virtual void report_unhandled_exception(CallReport call, ExpectationReport expectationReport, std::exception_ptr exception)=0
Expects reports about unhandled exceptions, during handle_call.
virtual void report_error(StringT message)=0
Expects rather unspecific errors.
virtual void report_no_matches(CallReport call, std::vector< MatchReport > matchReports)=0
Expects reports about all none matching expectations. This is only called, if there are no better opt...
virtual void report_full_match(CallReport call, MatchReport matchReport) noexcept=0
Expects the report about a full matching expectation.
virtual ~IReporter()=default
Defaulted virtual destructor.
IReporter(IReporter &&)=default
IReporter & operator=(const IReporter &)=default
virtual void report_unfulfilled_expectation(ExpectationReport expectationReport)=0
Expects the report of an unfulfilled expectation.
virtual void report_inapplicable_matches(CallReport call, std::vector< MatchReport > matchReports)=0
Expects reports about all inapplicable matching expectations. This is only called,...
Contains the detailed information for match outcomes.
Definition Reports.hpp:407
MatchResult evaluate_match_report(const MatchReport &report) noexcept
Determines, whether a match report actually denotes a full, inapplicable or no match.
Definition Reports.hpp:458
void install_reporter(Args &&... args)
Replaces the previous reporter with a newly constructed one.
Definition Reporter.hpp:556
constexpr detail::PrintFn print
Functional object, converting the given object to its textual representation.
Definition Printer.hpp:537
Definition BoostTest.hpp:20
void unreachable()
Invokes undefined behavior.
Definition Utility.hpp:93
std::basic_ostringstream< CharT, CharTraitsT > StringStreamT
Definition Printer.hpp:41
std::basic_string< CharT, CharTraitsT > StringT
Definition Fwd.hpp:112