mimic++ v6
Loading...
Searching...
No Matches
Reports.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_REPORTS_HPP
7#define MIMICPP_REPORTS_HPP
8
9#pragma once
10
11#include "mimic++/Call.hpp"
12#include "mimic++/Fwd.hpp"
13#include "mimic++/Printer.hpp"
14
15#include <algorithm>
16#include <functional>
17#include <iterator>
18#include <optional>
19#include <ranges>
20#include <typeindex>
21#include <variant>
22#include <vector>
23
24namespace mimicpp
25{
38
40 {
41 int min{};
42 int max{};
43 int count{};
44 std::vector<sequence::rating> sequenceRatings{};
45 std::vector<sequence::Tag> inapplicableSequences{};
46
47 [[nodiscard]]
48 friend bool operator==(const state_inapplicable&, const state_inapplicable&) = default;
49 };
50
52 {
53 int min{};
54 int max{};
55 int count{};
56 std::vector<sequence::rating> sequenceRatings{};
57
58 [[nodiscard]]
59 friend bool operator==(const state_applicable&, const state_applicable&) = default;
60 };
61
63 {
64 int min{};
65 int max{};
66 int count{};
67 std::vector<sequence::Tag> sequences{};
68
69 [[nodiscard]]
70 friend bool operator==(const state_saturated&, const state_saturated&) = default;
71 };
72
73 using control_state_t = std::variant<
77
78 namespace detail
79 {
80 template <print_iterator OutIter>
81 OutIter print_times_state(OutIter out, const std::size_t current, const std::size_t min, const std::size_t max)
82 {
83 const auto verbalizeValue = [](OutIter o, const std::size_t value) {
84 switch (value)
85 {
86 case 0: return format::format_to(std::move(o), "never");
87 case 1: return format::format_to(std::move(o), "once");
88 case 2: return format::format_to(std::move(o), "twice");
89 default: return format::format_to(std::move(o), "{} times", value);
90 }
91 };
92
93 if (current == max)
94 {
95 out = format::format_to(
96 std::move(out),
97 "already saturated (matched ");
98 out = verbalizeValue(std::move(out), current);
99 return format::format_to(std::move(out), ")");
100 }
101
102 if (min <= current)
103 {
104 return format::format_to(
105 std::move(out),
106 "accepts further matches (matched {} out of {} times)",
107 current,
108 max);
109 }
110
111 const auto verbalizeInterval = [verbalizeValue](OutIter o, const std::size_t start, const std::size_t end) {
112 if (start < end)
113 {
114 return format::format_to(
115 std::move(o),
116 "between {} and {} times",
117 start,
118 end);
119 }
120
121 o = format::format_to(std::move(o), "exactly ");
122 return verbalizeValue(std::move(o), end);
123 };
124
125 out = format::format_to(std::move(out), "matched ");
126 out = verbalizeValue(std::move(out), current);
127 out = format::format_to(std::move(out), " - ");
128 out = verbalizeInterval(std::move(out), min, max);
129 return format::format_to(std::move(out), " is expected");
130 }
131
132 struct control_state_printer
133 {
134 template <print_iterator OutIter>
135 OutIter operator()(OutIter out, const state_applicable& state) const
136 {
137 out = print_times_state(
138 std::move(out),
139 state.count,
140 state.min,
141 state.max);
142
143 if (const auto sequenceCount = std::ranges::ssize(state.sequenceRatings);
144 0 < sequenceCount)
145 {
146 out = format::format_to(
147 std::move(out),
148 ",\n\tand is the current element of {} sequence(s).",
149 sequenceCount);
150 }
151
152 return out;
153 }
154
155 template <print_iterator OutIter>
156 OutIter operator()(OutIter out, const state_inapplicable& state) const
157 {
158 out = print_times_state(
159 std::move(out),
160 state.count,
161 state.min,
162 state.max);
163
164 const auto totalSequences = std::ranges::ssize(state.sequenceRatings)
165 + std::ranges::ssize(state.inapplicableSequences);
166 return format::format_to(
167 std::move(out),
168 ",\n\tbut is not the current element of {} sequence(s) ({} total).",
169 std::ranges::ssize(state.inapplicableSequences),
170 totalSequences);
171 }
172
173 template <print_iterator OutIter>
174 OutIter operator()(OutIter out, const state_saturated& state) const
175 {
176 out = print_times_state(
177 std::move(out),
178 state.count,
179 state.min,
180 state.max);
181
182 if (const auto sequenceCount = std::ranges::ssize(state.sequences);
183 0 < sequenceCount)
184 {
185 out = format::format_to(
186 std::move(out),
187 ",\n\tand is part of {} sequence(s).",
188 sequenceCount);
189 }
190
191 return out;
192 }
193 };
194 }
195
203 {
204 public:
205 class Arg
206 {
207 public:
208 std::type_index typeIndex;
210
211 [[nodiscard]]
212 friend bool operator==(const Arg&, const Arg&) = default;
213 };
214
215 std::type_index returnTypeIndex;
216 std::vector<Arg> argDetails{};
217 std::source_location fromLoc{};
221
222 [[nodiscard]]
223 friend bool operator==(const CallReport& lhs, const CallReport& rhs)
224 {
225 return lhs.returnTypeIndex == rhs.returnTypeIndex
226 && lhs.argDetails == rhs.argDetails
228 && lhs.fromCategory == rhs.fromCategory
229 && lhs.fromConstness == rhs.fromConstness
230 && lhs.stacktrace == rhs.stacktrace;
231 }
232 };
233
242 template <typename Return, typename... Params>
243 [[nodiscard]]
245 {
246 return CallReport{
247 .returnTypeIndex = typeid(Return),
248 .argDetails = std::apply(
249 [](auto&... args) {
250 return std::vector<CallReport::Arg>{
252 .typeIndex = typeid(Params),
253 .stateString = mimicpp::print(args.get())}
254 ...
255 };
256 },
257 callInfo.args),
258 .fromLoc = std::move(callInfo.fromSourceLocation),
259 .stacktrace = std::move(callInfo.stacktrace),
260 .fromCategory = callInfo.fromCategory,
261 .fromConstness = callInfo.fromConstness};
262 }
263
264 template <>
265 class detail::Printer<CallReport>
266 {
267 public:
268 template <print_iterator OutIter>
269 static OutIter print(OutIter out, const CallReport& report)
270 {
271 out = format::format_to(
272 std::move(out),
273 "call from ");
274 if (!report.stacktrace.empty())
275 {
276 out = format::format_to(
277 std::move(out),
278 "{} [{}], {}",
279 report.stacktrace.source_file(0u),
280 report.stacktrace.source_line(0u),
281 report.stacktrace.description(0u));
282 }
283 else
284 {
285 out = mimicpp::print(
286 std::move(out),
287 report.fromLoc);
288 }
289 out = format::format_to(
290 std::move(out),
291 "\n");
292
293 out = format::format_to(
294 std::move(out),
295 "constness: {}\n"
296 "value category: {}\n"
297 "return type: {}\n",
298 report.fromConstness,
299 report.fromCategory,
300 report.returnTypeIndex.name());
301
302 if (!std::ranges::empty(report.argDetails))
303 {
304 out = format::format_to(
305 std::move(out),
306 "args:\n");
307 for (const std::size_t i : std::views::iota(0u, std::ranges::size(report.argDetails)))
308 {
309 out = format::format_to(
310 std::move(out),
311 "\targ[{}]: {{\n"
312 "\t\ttype: {},\n"
313 "\t\tvalue: {}\n"
314 "\t}},\n",
315 i,
316 report.argDetails[i].typeIndex.name(),
317 report.argDetails[i].stateString);
318 }
319 }
320
321 return out;
322 }
323 };
324
331 {
332 public:
333 std::optional<std::source_location> sourceLocation{};
334 std::optional<StringT> finalizerDescription{};
335 std::optional<StringT> timesDescription{};
336 std::vector<std::optional<StringT>> expectationDescriptions{};
337
338 [[nodiscard]]
339 friend bool operator==(const ExpectationReport& lhs, const ExpectationReport& rhs)
340 {
344 && lhs.sourceLocation.has_value() == rhs.sourceLocation.has_value()
345 && (!lhs.sourceLocation.has_value()
347 }
348 };
349
350 template <>
351 class detail::Printer<ExpectationReport>
352 {
353 public:
354 template <print_iterator OutIter>
355 static OutIter print(OutIter out, const ExpectationReport& report)
356 {
357 out = format::format_to(
358 std::move(out),
359 "Expectation report:\n");
360
361 if (report.sourceLocation)
362 {
363 out = format::format_to(
364 std::move(out),
365 "from: ");
366 out = mimicpp::print(
367 std::move(out),
368 *report.sourceLocation);
369 out = format::format_to(
370 std::move(out),
371 "\n");
372 }
373
374 if (report.timesDescription)
375 {
376 out = format::format_to(
377 out,
378 "times: {}\n",
379 *report.timesDescription);
380 }
381
382 if (std::ranges::any_of(
383 report.expectationDescriptions,
384 [](const auto& desc) { return desc.has_value(); }))
385 {
386 out = format::format_to(
387 std::move(out),
388 "expects:\n");
389 for (const auto& desc : report.expectationDescriptions
390 | std::views::filter([](const auto& desc) { return desc.has_value(); }))
391 {
392 out = format::format_to(
393 std::move(out),
394 "\t{},\n",
395 *desc);
396 }
397 }
398
399 if (report.finalizerDescription)
400 {
401 out = format::format_to(
402 std::move(out),
403 "finally: {}\n",
404 *report.finalizerDescription);
405 }
406
407 return out;
408 }
409 };
410
417 {
418 public:
423 {
424 public:
425 std::optional<StringT> description{};
426
427 [[nodiscard]]
428 friend bool operator==(const Finalize&, const Finalize&) = default;
429 };
430
436 {
437 public:
439 std::optional<StringT> description{};
440
441 [[nodiscard]]
442 friend bool operator==(const Expectation&, const Expectation&) = default;
443 };
444
445 std::optional<std::source_location> sourceLocation{};
448 std::vector<Expectation> expectationReports{};
449
450 [[nodiscard]]
451 friend bool operator==(const MatchReport& lhs, const MatchReport& rhs)
452 {
453 return lhs.finalizeReport == rhs.finalizeReport
454 && lhs.controlReport == rhs.controlReport
456 && lhs.sourceLocation.has_value() == rhs.sourceLocation.has_value()
457 && (!lhs.sourceLocation.has_value()
459 }
460 };
461
467 [[nodiscard]]
468 inline MatchResult evaluate_match_report(const MatchReport& report) noexcept
469 {
470 if (!std::ranges::all_of(report.expectationReports, &MatchReport::Expectation::isMatching))
471 {
472 return MatchResult::none;
473 }
474
475 if (!std::holds_alternative<state_applicable>(report.controlReport))
476 {
478 }
479
480 return MatchResult::full;
481 }
482
483 template <>
484 class detail::Printer<MatchReport>
485 {
486 public:
487 template <print_iterator OutIter>
488 static OutIter print(OutIter out, const MatchReport& report)
489 {
490 std::vector<StringT> matchedExpectationDescriptions{};
491 std::vector<StringT> unmatchedExpectationDescriptions{};
492
493 for (const auto& [isMatching, description] : report.expectationReports)
494 {
495 if (description)
496 {
497 if (isMatching)
498 {
499 matchedExpectationDescriptions.emplace_back(*description);
500 }
501 else
502 {
503 unmatchedExpectationDescriptions.emplace_back(*description);
504 }
505 }
506 }
507
508 switch (evaluate_match_report(report))
509 {
511 out = format::format_to(
512 std::move(out),
513 "Matched expectation: {{\n");
514 break;
515
517 out = format::format_to(
518 std::move(out),
519 "Inapplicable, but otherwise matched expectation: {{\n"
520 "reason: ");
521 out = std::visit(
522 std::bind_front(control_state_printer{}, std::move(out)),
523 report.controlReport);
524 out = format::format_to(std::move(out), "\n");
525 break;
526
528 out = format::format_to(
529 std::move(out),
530 "Unmatched expectation: {{\n");
531 break;
532
533 // GCOVR_EXCL_START
534 default: // NOLINT(clang-diagnostic-covered-switch-default)
535 unreachable();
536 // GCOVR_EXCL_STOP
537 }
538
539 if (report.sourceLocation)
540 {
541 out = format::format_to(
542 std::move(out),
543 "from: ");
544 out = mimicpp::print(
545 std::move(out),
546 *report.sourceLocation);
547 out = format::format_to(
548 std::move(out),
549 "\n");
550 }
551
552 if (!std::ranges::empty(unmatchedExpectationDescriptions))
553 {
554 out = format::format_to(
555 std::move(out),
556 "failed:\n");
557 for (const auto& desc : unmatchedExpectationDescriptions)
558 {
559 out = format::format_to(
560 std::move(out),
561 "\t{},\n",
562 desc);
563 }
564 }
565
566 if (!std::ranges::empty(matchedExpectationDescriptions))
567 {
568 out = format::format_to(
569 std::move(out),
570 "passed:\n");
571 for (const auto& desc : matchedExpectationDescriptions)
572 {
573 out = format::format_to(
574 std::move(out),
575 "\t{},\n",
576 desc);
577 }
578 }
579
580 return format::format_to(
581 std::move(out),
582 "}}\n");
583 }
584 };
585
589}
590
591#endif
Definition Reports.hpp:206
std::type_index typeIndex
Definition Reports.hpp:208
friend bool operator==(const Arg &, const Arg &)=default
StringT stateString
Definition Reports.hpp:209
Contains the extracted info from a typed call::Info.
Definition Reports.hpp:203
std::type_index returnTypeIndex
Definition Reports.hpp:215
friend bool operator==(const CallReport &lhs, const CallReport &rhs)
Definition Reports.hpp:223
Constness fromConstness
Definition Reports.hpp:220
ValueCategory fromCategory
Definition Reports.hpp:219
std::vector< Arg > argDetails
Definition Reports.hpp:216
Stacktrace stacktrace
Definition Reports.hpp:218
std::source_location fromLoc
Definition Reports.hpp:217
Contains the extracted info from a typed expectation.
Definition Reports.hpp:331
std::optional< StringT > finalizerDescription
Definition Reports.hpp:334
std::optional< StringT > timesDescription
Definition Reports.hpp:335
std::optional< std::source_location > sourceLocation
Definition Reports.hpp:333
std::vector< std::optional< StringT > > expectationDescriptions
Definition Reports.hpp:336
friend bool operator==(const ExpectationReport &lhs, const ExpectationReport &rhs)
Definition Reports.hpp:339
Information a used expectation policy.
Definition Reports.hpp:436
std::optional< StringT > description
Definition Reports.hpp:439
friend bool operator==(const Expectation &, const Expectation &)=default
bool isMatching
Definition Reports.hpp:438
Information about the used finalizer.
Definition Reports.hpp:423
std::optional< StringT > description
Definition Reports.hpp:425
friend bool operator==(const Finalize &, const Finalize &)=default
Contains the detailed information for match outcomes.
Definition Reports.hpp:417
std::vector< Expectation > expectationReports
Definition Reports.hpp:448
Finalize finalizeReport
Definition Reports.hpp:446
friend bool operator==(const MatchReport &lhs, const MatchReport &rhs)
Definition Reports.hpp:451
control_state_t controlReport
Definition Reports.hpp:447
std::optional< std::source_location > sourceLocation
Definition Reports.hpp:445
A simple type-erase stacktrace abstraction.
Definition Stacktrace.hpp:173
constexpr std::string description(const std::size_t at) const
Queries the underlying stacktrace-backend for the description of the selected stacktrace-entry.
Definition Stacktrace.hpp:254
constexpr bool empty() const
Queries the underlying stacktrace-backend whether its empty.
Definition Stacktrace.hpp:243
constexpr std::size_t source_line(const std::size_t at) const
Queries the underlying stacktrace-backend for the source-line of the selected stacktrace-entry.
Definition Stacktrace.hpp:276
constexpr std::string source_file(const std::size_t at) const
Queries the underlying stacktrace-backend for the source-file of the selected stacktrace-entry.
Definition Stacktrace.hpp:265
Definition Fwd.hpp:19
ValueCategory fromCategory
Definition Call.hpp:28
Stacktrace stacktrace
Definition Call.hpp:31
std::source_location fromSourceLocation
Definition Call.hpp:30
ArgListT args
Definition Call.hpp:27
Constness fromConstness
Definition Call.hpp:29
The fallback stacktrace-backend.
Definition Stacktrace.hpp:412
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
CallReport make_call_report(call::Info< Return, Params... > callInfo)
Generates the call report for a given call info.
Definition Reports.hpp:244
std::variant< state_inapplicable, state_applicable, state_saturated > control_state_t
Definition Reports.hpp:73
constexpr detail::PrintFn print
Functional object, converting the given object to its textual representation.
Definition Printer.hpp:590
Definition BoostTest.hpp:20
MatchResult
Definition Fwd.hpp:332
@ inapplicable
Definition Fwd.hpp:334
@ none
Definition Fwd.hpp:333
@ full
Definition Fwd.hpp:335
void unreachable()
Invokes undefined behavior.
Definition Utility.hpp:91
constexpr bool is_same_source_location(const std::source_location &lhs, const std::source_location &rhs) noexcept
Definition Utility.hpp:42
ValueCategory
Definition Fwd.hpp:32
Constness
Definition Fwd.hpp:25
std::basic_string< CharT, CharTraitsT > StringT
Definition Fwd.hpp:344
Definition Reports.hpp:52
std::vector< sequence::rating > sequenceRatings
Definition Reports.hpp:56
int count
Definition Reports.hpp:55
int min
Definition Reports.hpp:53
friend bool operator==(const state_applicable &, const state_applicable &)=default
int max
Definition Reports.hpp:54
Definition Reports.hpp:40
std::vector< sequence::rating > sequenceRatings
Definition Reports.hpp:44
int count
Definition Reports.hpp:43
friend bool operator==(const state_inapplicable &, const state_inapplicable &)=default
int min
Definition Reports.hpp:41
int max
Definition Reports.hpp:42
std::vector< sequence::Tag > inapplicableSequences
Definition Reports.hpp:45
Definition Reports.hpp:63
int max
Definition Reports.hpp:65
std::vector< sequence::Tag > sequences
Definition Reports.hpp:67
int count
Definition Reports.hpp:66
friend bool operator==(const state_saturated &, const state_saturated &)=default
int min
Definition Reports.hpp:64