mimic++ v5
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) = 0;
78
90 [[noreturn]]
92 CallReport call,
93 std::vector<MatchReport> matchReports) = 0;
94
102 virtual void report_full_match(
103 CallReport call,
104 MatchReport matchReport) noexcept = 0;
105
118 ExpectationReport expectationReport) = 0;
119
130 virtual void report_error(StringT message) = 0;
131
141 CallReport call,
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 [[nodiscard]]
191 inline StringT stringify_no_match_report(const CallReport& call, const std::span<const MatchReport> matchReports)
192 {
193 StringStreamT ss{};
194 ss << "No match for ";
196 std::ostreambuf_iterator{ss},
197 call);
198 ss << "\n";
199
200 if (std::ranges::empty(matchReports))
201 {
202 ss << "No expectations available.\n";
203 }
204 else
205 {
206 format::format_to(
207 std::ostreambuf_iterator{ss},
208 "{} available expectation(s):\n",
209 std::ranges::size(matchReports));
210
211 for (const auto& report : matchReports)
212 {
214 std::ostreambuf_iterator{ss},
215 report);
216 ss << "\n";
217 }
218 }
219
220 return std::move(ss).str();
221 }
222
223 [[nodiscard]]
224 inline StringT stringify_inapplicable_match_report(const CallReport& call, const std::span<const MatchReport> matchReports)
225 {
226 StringStreamT ss{};
227 ss << "No applicable match for ";
229 std::ostreambuf_iterator{ss},
230 call);
231 ss << "\n";
232
233 ss << "Tested expectations:\n";
234 for (const auto& report : matchReports)
235 {
237 std::ostreambuf_iterator{ss},
238 report);
239 ss << "\n";
240 }
241
242 return std::move(ss).str();
243 }
244
245 [[nodiscard]]
246 inline StringT stringify_report(const CallReport& call, const MatchReport& matchReport)
247 {
248 StringStreamT ss{};
249 ss << "Found match for ";
251 std::ostreambuf_iterator{ss},
252 call);
253 ss << "\n";
254
256 std::ostreambuf_iterator{ss},
257 matchReport);
258 ss << "\n";
259
260 return std::move(ss).str();
261 }
262
263 [[nodiscard]]
264 inline StringT stringify_unfulfilled_expectation(const ExpectationReport& expectationReport)
265 {
266 StringStreamT ss{};
267 ss << "Unfulfilled expectation:\n";
269 std::ostreambuf_iterator{ss},
270 expectationReport);
271 ss << "\n";
272
273 return std::move(ss).str();
274 }
275
276 [[nodiscard]]
277 inline StringT stringify_unhandled_exception(
278 const CallReport& call,
279 const ExpectationReport& expectationReport,
280 const std::exception_ptr& exception)
281 {
282 StringStreamT ss{};
283 ss << "Unhandled exception: ";
284
285 try
286 {
287 std::rethrow_exception(exception);
288 }
289 catch (const std::exception& e)
290 {
291 ss << "what: "
292 << e.what()
293 << "\n";
294 }
295 catch (...)
296 {
297 ss << "Unknown exception type.\n";
298 }
299
300 ss << "while checking expectation:\n";
302 std::ostreambuf_iterator{ss},
303 expectationReport);
304 ss << "\n";
305
306 ss << "For ";
308 std::ostreambuf_iterator{ss},
309 call);
310 ss << "\n";
311
312 return std::move(ss).str();
313 }
314 }
315
322 template <
323 std::invocable<const StringT&> auto successReporter,
324 std::invocable<const StringT&> auto warningReporter,
325 std::invocable<const StringT&> auto failReporter>
327 : public IReporter
328 {
329 public:
330 [[noreturn]]
331 void report_no_matches(const CallReport call, const std::vector<MatchReport> matchReports) override
332 {
333 send_fail(
334 detail::stringify_no_match_report(call, matchReports));
335 }
336
337 [[noreturn]]
338 void report_inapplicable_matches(const CallReport call, const std::vector<MatchReport> matchReports) override
339 {
340 send_fail(
341 detail::stringify_inapplicable_match_report(call, matchReports));
342 }
343
344 void report_full_match(const CallReport call, const MatchReport matchReport) noexcept override
345 {
346 send_success(
347 detail::stringify_report(call, matchReport));
348 }
349
350 void report_unfulfilled_expectation(const ExpectationReport expectationReport) override
351 {
352 if (0 == std::uncaught_exceptions())
353 {
354 send_fail(
355 detail::stringify_unfulfilled_expectation(expectationReport));
356 }
357 }
358
359 void report_error(const StringT message) override
360 {
361 if (0 == std::uncaught_exceptions())
362 {
363 send_fail(message);
364 }
365 }
366
368 const CallReport call,
369 const ExpectationReport expectationReport,
370 const std::exception_ptr exception) override
371 {
372 send_warning(
373 detail::stringify_unhandled_exception(call, expectationReport, exception));
374 }
375
376 private:
377 void send_success(const StringT& msg)
378 {
379 std::invoke(successReporter, msg);
380 }
381
382 void send_warning(const StringT& msg)
383 {
384 std::invoke(warningReporter, msg);
385 }
386
387 [[noreturn]]
388 void send_fail(const StringT& msg)
389 {
390 // GCOVR_EXCL_START
391 std::invoke(failReporter, msg);
392 unreachable();
393 // GCOVR_EXCL_STOP
394 }
395 };
396
399
403 class DefaultReporter final
404 : public IReporter
405 {
406 public:
407 [[nodiscard]]
408 explicit DefaultReporter(std::ostream* out = nullptr) noexcept
409 : m_Out{out}
410 {
411 }
412
413 [[noreturn]]
415 CallReport call,
416 std::vector<MatchReport> matchReports) override
417 {
418 assert(
419 std::ranges::all_of(
420 matchReports,
421 std::bind_front(std::equal_to{}, MatchResult::none),
423
424 const auto msg = detail::stringify_no_match_report(call, matchReports);
425 if (m_Out)
426 {
427 *m_Out << msg << '\n';
428 }
429
430 const std::source_location loc{call.fromLoc};
431 throw UnmatchedCallT{
432 msg,
433 {std::move(call), std::move(matchReports)},
434 loc
435 };
436 }
437
438 [[noreturn]]
440 CallReport call,
441 std::vector<MatchReport> matchReports) override
442 {
443 assert(
444 std::ranges::all_of(
445 matchReports,
446 std::bind_front(std::equal_to{}, MatchResult::inapplicable),
448
449 const auto msg = detail::stringify_inapplicable_match_report(call, matchReports);
450 if (m_Out)
451 {
452 *m_Out << msg << '\n';
453 }
454
455 const std::source_location loc{call.fromLoc};
456 throw UnmatchedCallT{
457 msg,
458 {std::move(call), std::move(matchReports)},
459 loc
460 };
461 }
462
464 [[maybe_unused]] const CallReport call,
465 [[maybe_unused]] const MatchReport matchReport) noexcept override
466 {
467 assert(MatchResult::full == evaluate_match_report(matchReport));
468 }
469
471 ExpectationReport expectationReport) override
472 {
473 if (0 == std::uncaught_exceptions())
474 {
475 const auto msg = detail::stringify_unfulfilled_expectation(expectationReport);
476 if (m_Out)
477 {
478 *m_Out << msg << '\n';
479 }
480
482 msg,
483 std::move(expectationReport)};
484 }
485 }
486
487 void report_error(const StringT message) override
488 {
489 if (0 == std::uncaught_exceptions())
490 {
491 if (m_Out)
492 {
493 *m_Out << message << '\n';
494 }
495
496 throw Error{message};
497 }
498 }
499
501 const CallReport call,
502 const ExpectationReport expectationReport,
503 const std::exception_ptr exception) override
504 {
505 if (m_Out)
506 {
507 *m_Out << detail::stringify_unhandled_exception(call, expectationReport, exception)
508 << '\n';
509 }
510 }
511
512 private:
513 std::ostream* m_Out;
514 };
515
519}
520
521namespace mimicpp::detail
522{
523 [[nodiscard]]
524 inline std::unique_ptr<IReporter>& get_reporter() noexcept
525 {
526 static std::unique_ptr<IReporter> reporter{
527 std::make_unique<DefaultReporter>(&std::cerr)};
528 return reporter;
529 }
530
531 [[noreturn]]
532 inline void report_no_matches(
533 CallReport callReport,
534 std::vector<MatchReport> matchReports)
535 {
536 get_reporter()
537 // GCOVR_EXCL_START
538 ->report_no_matches(
539 // GCOVR_EXCL_STOP
540 std::move(callReport),
541 std::move(matchReports));
542
543 // GCOVR_EXCL_START
544 // ReSharper disable once CppUnreachableCode
545 unreachable();
546 // GCOVR_EXCL_STOP
547 }
548
549 [[noreturn]]
550 inline void report_inapplicable_matches(
551 CallReport callReport,
552 std::vector<MatchReport> matchReports)
553 {
554 get_reporter()
555 // GCOVR_EXCL_START
556 ->report_inapplicable_matches(
557 // GCOVR_EXCL_STOP
558 std::move(callReport),
559 std::move(matchReports));
560
561 // GCOVR_EXCL_START
562 // ReSharper disable once CppUnreachableCode
563 unreachable();
564 // GCOVR_EXCL_STOP
565 }
566
567 inline void report_full_match(
568 CallReport callReport,
569 MatchReport matchReport) noexcept
570 {
571 get_reporter()
572 ->report_full_match(
573 std::move(callReport),
574 std::move(matchReport));
575 }
576
577 inline void report_unfulfilled_expectation(
578 ExpectationReport expectationReport)
579 {
580 get_reporter()
581 ->report_unfulfilled_expectation(std::move(expectationReport));
582 }
583
584 inline void report_error(StringT message)
585 {
586 get_reporter()
587 ->report_error(std::move(message));
588 }
589
590 inline void report_unhandled_exception(
591 CallReport callReport,
592 ExpectationReport expectationReport,
593 const std::exception_ptr& exception)
594 {
595 get_reporter()
596 ->report_unhandled_exception(
597 std::move(callReport),
598 std::move(expectationReport),
599 exception);
600 }
601}
602
603namespace mimicpp
604{
613 template <std::derived_from<IReporter> T, typename... Args>
614 requires std::constructible_from<T, Args...>
615 void install_reporter(Args&&... args) // NOLINT(cppcoreguidelines-missing-std-forward)
616 {
617 detail::get_reporter() = std::make_unique<T>(
618 std::forward<Args>(args)...);
619 }
620
621 namespace detail
622 {
623 template <typename T>
624 class ReporterInstaller
625 {
626 public:
627 template <typename... Args>
628 explicit ReporterInstaller(Args&&... args)
629 {
631 std::forward<Args>(args)...);
632 }
633 };
634 }
635
650}
651
652#endif
A reporter, which creates text messages and reports them via the provided callbacks.
Definition Reporter.hpp:328
void report_error(const StringT message) override
Expects rather unspecific errors.
Definition Reporter.hpp:359
void report_full_match(const CallReport call, const MatchReport matchReport) noexcept override
Expects the report about a full matching expectation.
Definition Reporter.hpp:344
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:367
void report_unfulfilled_expectation(const ExpectationReport expectationReport) override
Expects the report of an unfulfilled expectation.
Definition Reporter.hpp:350
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:331
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:338
Contains the extracted info from a typed call::Info.
Definition Reports.hpp:203
std::source_location fromLoc
Definition Reports.hpp:217
The default reporter.
Definition Reporter.hpp:405
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:500
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:414
DefaultReporter(std::ostream *out=nullptr) noexcept
Definition Reporter.hpp:408
void report_unfulfilled_expectation(ExpectationReport expectationReport) override
Expects the report of an unfulfilled expectation.
Definition Reporter.hpp:470
void report_full_match(const CallReport call, const MatchReport matchReport) noexcept override
Expects the report about a full matching expectation.
Definition Reporter.hpp:463
void report_error(const StringT message) override
Expects rather unspecific errors.
Definition Reporter.hpp:487
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:439
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 Data & 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:316
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,...
IReporter & operator=(IReporter &&)=default
Contains the detailed information for match outcomes.
Definition Reports.hpp:402
MatchResult evaluate_match_report(const MatchReport &report) noexcept
Determines, whether a match report actually denotes a full, inapplicable or no match.
Definition Reports.hpp:453
void install_reporter(Args &&... args)
Replaces the previous reporter with a newly constructed one.
Definition Reporter.hpp:615
constexpr detail::PrintFn print
Functional object, converting the given object to its textual representation.
Definition Printer.hpp:593
Definition BoostTest.hpp:20
void unreachable()
Invokes undefined behavior.
Definition Utility.hpp:89
std::basic_ostringstream< CharT, CharTraitsT > StringStreamT
Definition Printer.hpp:41
std::basic_string< CharT, CharTraitsT > StringT
Definition Fwd.hpp:342