mimic++ v6
Loading...
Searching...
No Matches
Reporter.hpp
Go to the documentation of this file.
1// Copyright Dominic (DNKpp) Koepke 2024 - 2025.
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 <iostream>
21#include <memory>
22#include <optional>
23#include <ranges>
24#include <vector>
25
26namespace mimicpp
27{
50
56 {
57 public:
61 virtual ~IReporter() = default;
62
74 [[noreturn]]
75 virtual void report_no_matches(
77 std::vector<MatchReport> matchReports) = 0;
78
90 [[noreturn]]
93 std::vector<MatchReport> matchReports) = 0;
94
102 virtual void report_full_match(
104 MatchReport matchReport) noexcept = 0;
105
118 ExpectationReport expectationReport) = 0;
119
130 virtual void report_error(StringT message) = 0;
131
142 ExpectationReport expectationReport,
143 std::exception_ptr exception) = 0;
144
145 protected:
146 [[nodiscard]]
147 IReporter() = default;
148
149 IReporter(const IReporter&) = default;
150 IReporter& operator=(const IReporter&) = default;
151 IReporter(IReporter&&) = default;
153 };
154
155 template <typename Data = std::nullptr_t>
156 class Error final
157 : public std::runtime_error
158 {
159 public:
160 [[nodiscard]]
161 explicit Error(
162 const std::string& what,
163 Data&& data = Data{},
164 const std::source_location& loc = std::source_location::current())
165 : std::runtime_error{what},
166 m_Data{std::move(data)},
167 m_Loc{loc}
168 {
169 }
170
171 [[nodiscard]]
172 const Data& data() const noexcept
173 {
174 return m_Data;
175 }
176
177 [[nodiscard]]
178 const std::source_location& where() const noexcept
179 {
180 return m_Loc;
181 }
182
183 private:
184 Data m_Data;
185 std::source_location m_Loc;
186 };
187
188 namespace detail
189 {
190 template <print_iterator OutIter>
191 OutIter stringify_stacktrace(OutIter out, const Stacktrace& stacktrace)
192 {
193 if (!stacktrace.empty())
194 {
195 out = format::format_to(
196 std::move(out),
197 "Stacktrace:\n");
198 out = mimicpp::print(
199 std::move(out),
200 stacktrace);
201 }
202
203 return out;
204 }
205
206 [[nodiscard]]
207 inline StringT stringify_no_match_report(const CallReport& call, const std::span<const MatchReport> matchReports)
208 {
209 StringStreamT ss{};
210 ss << "No match for ";
212 std::ostreambuf_iterator{ss},
213 call);
214 ss << "\n";
215
216 if (std::ranges::empty(matchReports))
217 {
218 ss << "No expectations available.\n";
219 }
220 else
221 {
222 format::format_to(
223 std::ostreambuf_iterator{ss},
224 "{} available expectation(s):\n",
225 std::ranges::size(matchReports));
226
227 for (const auto& report : matchReports)
228 {
230 std::ostreambuf_iterator{ss},
231 report);
232 ss << "\n";
233 }
234 }
235
236 stringify_stacktrace(
237 std::ostreambuf_iterator{ss},
238 call.stacktrace);
239
240 return std::move(ss).str();
241 }
242
243 [[nodiscard]]
244 inline StringT stringify_inapplicable_match_report(const CallReport& call, const std::span<const MatchReport> matchReports)
245 {
246 StringStreamT ss{};
247 ss << "No applicable match for ";
249 std::ostreambuf_iterator{ss},
250 call);
251 ss << "\n";
252
253 ss << "Tested expectations:\n";
254 for (const auto& report : matchReports)
255 {
257 std::ostreambuf_iterator{ss},
258 report);
259 ss << "\n";
260 }
261
262 stringify_stacktrace(
263 std::ostreambuf_iterator{ss},
264 call.stacktrace);
265
266 return std::move(ss).str();
267 }
268
269 [[nodiscard]]
270 inline StringT stringify_report(const CallReport& call, const MatchReport& matchReport)
271 {
272 StringStreamT ss{};
273 ss << "Found match for ";
275 std::ostreambuf_iterator{ss},
276 call);
277 ss << "\n";
278
280 std::ostreambuf_iterator{ss},
281 matchReport);
282 ss << "\n";
283
284 stringify_stacktrace(
285 std::ostreambuf_iterator{ss},
286 call.stacktrace);
287
288 return std::move(ss).str();
289 }
290
291 [[nodiscard]]
292 inline StringT stringify_unfulfilled_expectation(const ExpectationReport& expectationReport)
293 {
294 StringStreamT ss{};
295 ss << "Unfulfilled expectation:\n";
297 std::ostreambuf_iterator{ss},
298 expectationReport);
299 ss << "\n";
300
301 return std::move(ss).str();
302 }
303
304 [[nodiscard]]
305 inline StringT stringify_unhandled_exception(
306 const CallReport& call,
307 const ExpectationReport& expectationReport,
308 const std::exception_ptr& exception)
309 {
310 StringStreamT ss{};
311 ss << "Unhandled exception: ";
312
313 try
314 {
315 std::rethrow_exception(exception);
316 }
317 catch (const std::exception& e)
318 {
319 ss << "what: "
320 << e.what()
321 << "\n";
322 }
323 catch (...)
324 {
325 ss << "Unknown exception type.\n";
326 }
327
328 ss << "while checking expectation:\n";
330 std::ostreambuf_iterator{ss},
331 expectationReport);
332 ss << "\n";
333
334 ss << "For ";
336 std::ostreambuf_iterator{ss},
337 call);
338 ss << "\n";
339
340 return std::move(ss).str();
341 }
342 }
343
350 template <
351 std::invocable<const StringT&> auto successReporter,
352 std::invocable<const StringT&> auto warningReporter,
353 std::invocable<const StringT&> auto failReporter>
355 : public IReporter
356 {
357 public:
358 [[noreturn]]
359 void report_no_matches(const CallReport call, const std::vector<MatchReport> matchReports) override
360 {
361 send_fail(
362 detail::stringify_no_match_report(call, matchReports));
363 }
364
365 [[noreturn]]
366 void report_inapplicable_matches(const CallReport call, const std::vector<MatchReport> matchReports) override
367 {
368 send_fail(
369 detail::stringify_inapplicable_match_report(call, matchReports));
370 }
371
372 void report_full_match(const CallReport call, const MatchReport matchReport) noexcept override
373 {
374 send_success(
375 detail::stringify_report(call, matchReport));
376 }
377
378 void report_unfulfilled_expectation(const ExpectationReport expectationReport) override
379 {
380 if (0 == std::uncaught_exceptions())
381 {
382 send_fail(
383 detail::stringify_unfulfilled_expectation(expectationReport));
384 }
385 }
386
387 void report_error(const StringT message) override
388 {
389 if (0 == std::uncaught_exceptions())
390 {
391 send_fail(message);
392 }
393 }
394
396 const CallReport call,
397 const ExpectationReport expectationReport,
398 const std::exception_ptr exception) override
399 {
400 send_warning(
401 detail::stringify_unhandled_exception(call, expectationReport, exception));
402 }
403
404 private:
405 void send_success(const StringT& msg)
406 {
407 std::invoke(successReporter, msg);
408 }
409
410 void send_warning(const StringT& msg)
411 {
412 std::invoke(warningReporter, msg);
413 }
414
415 [[noreturn]]
416 void send_fail(const StringT& msg)
417 {
418 // GCOVR_EXCL_START
419 std::invoke(failReporter, msg);
420 unreachable();
421 // GCOVR_EXCL_STOP
422 }
423 };
424
427
431 class DefaultReporter final
432 : public IReporter
433 {
434 public:
435 [[nodiscard]]
436 explicit DefaultReporter(std::ostream* out = nullptr) noexcept
437 : m_Out{out}
438 {
439 }
440
441 [[noreturn]]
444 std::vector<MatchReport> matchReports) override
445 {
446 assert(
447 std::ranges::all_of(
448 matchReports,
449 std::bind_front(std::equal_to{}, MatchResult::none),
451
452 const auto msg = detail::stringify_no_match_report(call, matchReports);
453 if (m_Out)
454 {
455 *m_Out << msg << '\n';
456 }
457
458 const std::source_location loc{call.fromLoc};
459 throw UnmatchedCallT{
460 msg,
461 {std::move(call), std::move(matchReports)},
462 loc
463 };
464 }
465
466 [[noreturn]]
469 std::vector<MatchReport> matchReports) override
470 {
471 assert(
472 std::ranges::all_of(
473 matchReports,
474 std::bind_front(std::equal_to{}, MatchResult::inapplicable),
476
477 const auto msg = detail::stringify_inapplicable_match_report(call, matchReports);
478 if (m_Out)
479 {
480 *m_Out << msg << '\n';
481 }
482
483 const std::source_location loc{call.fromLoc};
484 throw UnmatchedCallT{
485 msg,
486 {std::move(call), std::move(matchReports)},
487 loc
488 };
489 }
490
492 [[maybe_unused]] const CallReport call,
493 [[maybe_unused]] const MatchReport matchReport) noexcept override
494 {
495 assert(MatchResult::full == evaluate_match_report(matchReport));
496 }
497
499 ExpectationReport expectationReport) override
500 {
501 if (0 == std::uncaught_exceptions())
502 {
503 const auto msg = detail::stringify_unfulfilled_expectation(expectationReport);
504 if (m_Out)
505 {
506 *m_Out << msg << '\n';
507 }
508
510 msg,
511 std::move(expectationReport)};
512 }
513 }
514
515 void report_error(const StringT message) override
516 {
517 if (0 == std::uncaught_exceptions())
518 {
519 if (m_Out)
520 {
521 *m_Out << message << '\n';
522 }
523
524 throw Error{message};
525 }
526 }
527
529 const CallReport call,
530 const ExpectationReport expectationReport,
531 const std::exception_ptr exception) override
532 {
533 if (m_Out)
534 {
535 *m_Out << detail::stringify_unhandled_exception(call, expectationReport, exception)
536 << '\n';
537 }
538 }
539
540 private:
541 std::ostream* m_Out;
542 };
543
547}
548
549namespace mimicpp::detail
550{
551 [[nodiscard]]
552 inline std::unique_ptr<IReporter>& get_reporter() noexcept
553 {
554 static std::unique_ptr<IReporter> reporter{
555 std::make_unique<DefaultReporter>(&std::cerr)};
556 return reporter;
557 }
558
559 [[noreturn]]
560 inline void report_no_matches(
561 CallReport callReport,
562 std::vector<MatchReport> matchReports)
563 {
564 get_reporter()
565 // GCOVR_EXCL_START
566 ->report_no_matches(
567 // GCOVR_EXCL_STOP
568 std::move(callReport),
569 std::move(matchReports));
570
571 // GCOVR_EXCL_START
572 // ReSharper disable once CppUnreachableCode
573 unreachable();
574 // GCOVR_EXCL_STOP
575 }
576
577 [[noreturn]]
578 inline void report_inapplicable_matches(
579 CallReport callReport,
580 std::vector<MatchReport> matchReports)
581 {
582 get_reporter()
583 // GCOVR_EXCL_START
584 ->report_inapplicable_matches(
585 // GCOVR_EXCL_STOP
586 std::move(callReport),
587 std::move(matchReports));
588
589 // GCOVR_EXCL_START
590 // ReSharper disable once CppUnreachableCode
591 unreachable();
592 // GCOVR_EXCL_STOP
593 }
594
595 inline void report_full_match(
596 CallReport callReport,
597 MatchReport matchReport) noexcept
598 {
599 get_reporter()
600 ->report_full_match(
601 std::move(callReport),
602 std::move(matchReport));
603 }
604
605 inline void report_unfulfilled_expectation(
606 ExpectationReport expectationReport)
607 {
608 get_reporter()
609 ->report_unfulfilled_expectation(std::move(expectationReport));
610 }
611
612 inline void report_error(StringT message)
613 {
614 get_reporter()
615 ->report_error(std::move(message));
616 }
617
618 inline void report_unhandled_exception(
619 CallReport callReport,
620 ExpectationReport expectationReport,
621 const std::exception_ptr& exception)
622 {
623 get_reporter()
624 ->report_unhandled_exception(
625 std::move(callReport),
626 std::move(expectationReport),
627 exception);
628 }
629}
630
631namespace mimicpp
632{
641 template <std::derived_from<IReporter> T, typename... Args>
642 requires std::constructible_from<T, Args...>
643 void install_reporter(Args&&... args) // NOLINT(cppcoreguidelines-missing-std-forward)
644 {
645 detail::get_reporter() = std::make_unique<T>(
646 std::forward<Args>(args)...);
647 }
648
649 namespace detail
650 {
651 template <typename T>
652 class ReporterInstaller
653 {
654 public:
655 template <typename... Args>
656 explicit ReporterInstaller(Args&&... args)
657 {
659 std::forward<Args>(args)...);
660 }
661 };
662 }
663
678}
679
680#endif
A reporter, which creates text messages and reports them via the provided callbacks.
Definition Reporter.hpp:356
void report_error(const StringT message) override
Expects rather unspecific errors.
Definition Reporter.hpp:387
void report_full_match(const CallReport call, const MatchReport matchReport) noexcept override
Expects the report about a full matching expectation.
Definition Reporter.hpp:372
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:395
void report_unfulfilled_expectation(const ExpectationReport expectationReport) override
Expects the report of an unfulfilled expectation.
Definition Reporter.hpp:378
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:359
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:366
Contains the extracted info from a typed call::Info.
Definition Reports.hpp:203
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:528
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:442
DefaultReporter(std::ostream *out=nullptr) noexcept
Definition Reporter.hpp:436
void report_unfulfilled_expectation(ExpectationReport expectationReport) override
Expects the report of an unfulfilled expectation.
Definition Reporter.hpp:498
void report_full_match(const CallReport call, const MatchReport matchReport) noexcept override
Expects the report about a full matching expectation.
Definition Reporter.hpp:491
void report_error(const StringT message) override
Expects rather unspecific errors.
Definition Reporter.hpp:515
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:467
Definition Reporter.hpp:158
Error(const std::string &what, Data &&data=Data{}, const std::source_location &loc=std::source_location::current())
Definition Reporter.hpp:161
const std::tuple< CallReport, std::vector< MatchReport > > & data() const noexcept
Definition Reporter.hpp:172
const std::source_location & where() const noexcept
Definition Reporter.hpp:178
Contains the extracted info from a typed expectation.
Definition Reports.hpp:331
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,...
IReporter & operator=(IReporter &&)=default
Contains the detailed information for match outcomes.
Definition Reports.hpp:417
MatchResult evaluate_match_report(const MatchReport &report) noexcept
Determines, whether a match report actually denotes a full, inapplicable or no match.
Definition Reports.hpp:468
void install_reporter(Args &&... args)
Replaces the previous reporter with a newly constructed one.
Definition Reporter.hpp:643
Error< std::tuple< CallReport, std::vector< MatchReport > > > UnmatchedCallT
Definition Reporter.hpp:425
Error< ExpectationReport > UnfulfilledExpectationT
Definition Reporter.hpp:426
constexpr detail::PrintFn print
Functional object, converting the given object to its textual representation.
Definition Printer.hpp:590
Definition Call.hpp:20
Definition BoostTest.hpp:20
@ inapplicable
Definition Fwd.hpp:334
@ none
Definition Fwd.hpp:333
@ full
Definition Fwd.hpp:335
void unreachable()
Invokes undefined behavior.
Definition Utility.hpp:91
std::basic_ostringstream< CharT, CharTraitsT > StringStreamT
Definition Printer.hpp:41
std::basic_string< CharT, CharTraitsT > StringT
Definition Fwd.hpp:344