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