mimic++ v5
Loading...
Searching...
No Matches
Reports.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_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{
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{};
220
221 [[nodiscard]]
222 friend bool operator==(const CallReport& lhs, const CallReport& rhs)
223 {
224 return lhs.returnTypeIndex == rhs.returnTypeIndex
225 && lhs.argDetails == rhs.argDetails
227 && lhs.fromCategory == rhs.fromCategory
228 && lhs.fromConstness == rhs.fromConstness;
229 }
230 };
231
240 template <typename Return, typename... Params>
241 [[nodiscard]]
243 {
244 return CallReport{
245 .returnTypeIndex = typeid(Return),
246 .argDetails = std::apply(
247 [](auto&... args) {
248 return std::vector<CallReport::Arg>{
250 .typeIndex = typeid(Params),
251 .stateString = mimicpp::print(args.get())}
252 ...
253 };
254 },
255 callInfo.args),
256 .fromLoc = callInfo.fromSourceLocation,
257 .fromCategory = callInfo.fromCategory,
258 .fromConstness = callInfo.fromConstness};
259 }
260
261 template <>
262 class detail::Printer<CallReport>
263 {
264 public:
265 template <print_iterator OutIter>
266 static OutIter print(OutIter out, const CallReport& report)
267 {
268 out = format::format_to(
269 std::move(out),
270 "call from ");
271 out = mimicpp::print(
272 std::move(out),
273 report.fromLoc);
274 out = format::format_to(
275 std::move(out),
276 "\n");
277
278 out = format::format_to(
279 std::move(out),
280 "constness: {}\n"
281 "value category: {}\n"
282 "return type: {}\n",
283 report.fromConstness,
284 report.fromCategory,
285 report.returnTypeIndex.name());
286
287 if (!std::ranges::empty(report.argDetails))
288 {
289 out = format::format_to(
290 std::move(out),
291 "args:\n");
292 for (const std::size_t i : std::views::iota(0u, std::ranges::size(report.argDetails)))
293 {
294 out = format::format_to(
295 std::move(out),
296 "\targ[{}]: {{\n"
297 "\t\ttype: {},\n"
298 "\t\tvalue: {}\n"
299 "\t}},\n",
300 i,
301 report.argDetails[i].typeIndex.name(),
302 report.argDetails[i].stateString);
303 }
304 }
305
306 return out;
307 }
308 };
309
316 {
317 public:
318 std::optional<std::source_location> sourceLocation{};
319 std::optional<StringT> finalizerDescription{};
320 std::optional<StringT> timesDescription{};
321 std::vector<std::optional<StringT>> expectationDescriptions{};
322
323 [[nodiscard]]
324 friend bool operator==(const ExpectationReport& lhs, const ExpectationReport& rhs)
325 {
329 && lhs.sourceLocation.has_value() == rhs.sourceLocation.has_value()
330 && (!lhs.sourceLocation.has_value()
332 }
333 };
334
335 template <>
336 class detail::Printer<ExpectationReport>
337 {
338 public:
339 template <print_iterator OutIter>
340 static OutIter print(OutIter out, const ExpectationReport& report)
341 {
342 out = format::format_to(
343 std::move(out),
344 "Expectation report:\n");
345
346 if (report.sourceLocation)
347 {
348 out = format::format_to(
349 std::move(out),
350 "from: ");
351 out = mimicpp::print(
352 std::move(out),
353 *report.sourceLocation);
354 out = format::format_to(
355 std::move(out),
356 "\n");
357 }
358
359 if (report.timesDescription)
360 {
361 out = format::format_to(
362 out,
363 "times: {}\n",
364 *report.timesDescription);
365 }
366
367 if (std::ranges::any_of(
368 report.expectationDescriptions,
369 [](const auto& desc) { return desc.has_value(); }))
370 {
371 out = format::format_to(
372 std::move(out),
373 "expects:\n");
374 for (const auto& desc : report.expectationDescriptions
375 | std::views::filter([](const auto& desc) { return desc.has_value(); }))
376 {
377 out = format::format_to(
378 std::move(out),
379 "\t{},\n",
380 *desc);
381 }
382 }
383
384 if (report.finalizerDescription)
385 {
386 out = format::format_to(
387 std::move(out),
388 "finally: {}\n",
389 *report.finalizerDescription);
390 }
391
392 return out;
393 }
394 };
395
402 {
403 public:
408 {
409 public:
410 std::optional<StringT> description{};
411
412 [[nodiscard]]
413 friend bool operator==(const Finalize&, const Finalize&) = default;
414 };
415
421 {
422 public:
424 std::optional<StringT> description{};
425
426 [[nodiscard]]
427 friend bool operator==(const Expectation&, const Expectation&) = default;
428 };
429
430 std::optional<std::source_location> sourceLocation{};
433 std::vector<Expectation> expectationReports{};
434
435 [[nodiscard]]
436 friend bool operator==(const MatchReport& lhs, const MatchReport& rhs)
437 {
438 return lhs.finalizeReport == rhs.finalizeReport
439 && lhs.controlReport == rhs.controlReport
441 && lhs.sourceLocation.has_value() == rhs.sourceLocation.has_value()
442 && (!lhs.sourceLocation.has_value()
444 }
445 };
446
452 [[nodiscard]]
453 inline MatchResult evaluate_match_report(const MatchReport& report) noexcept
454 {
455 if (!std::ranges::all_of(report.expectationReports, &MatchReport::Expectation::isMatching))
456 {
457 return MatchResult::none;
458 }
459
460 if (!std::holds_alternative<state_applicable>(report.controlReport))
461 {
463 }
464
465 return MatchResult::full;
466 }
467
468 template <>
469 class detail::Printer<MatchReport>
470 {
471 public:
472 template <print_iterator OutIter>
473 static OutIter print(OutIter out, const MatchReport& report)
474 {
475 std::vector<StringT> matchedExpectationDescriptions{};
476 std::vector<StringT> unmatchedExpectationDescriptions{};
477
478 for (const auto& [isMatching, description] : report.expectationReports)
479 {
480 if (description)
481 {
482 if (isMatching)
483 {
484 matchedExpectationDescriptions.emplace_back(*description);
485 }
486 else
487 {
488 unmatchedExpectationDescriptions.emplace_back(*description);
489 }
490 }
491 }
492
493 switch (evaluate_match_report(report))
494 {
496 out = format::format_to(
497 std::move(out),
498 "Matched expectation: {{\n");
499 break;
500
502 out = format::format_to(
503 std::move(out),
504 "Inapplicable, but otherwise matched expectation: {{\n"
505 "reason: ");
506 out = std::visit(
507 std::bind_front(control_state_printer{}, std::move(out)),
508 report.controlReport);
509 out = format::format_to(std::move(out), "\n");
510 break;
511
513 out = format::format_to(
514 std::move(out),
515 "Unmatched expectation: {{\n");
516 break;
517
518 // GCOVR_EXCL_START
519 default: // NOLINT(clang-diagnostic-covered-switch-default)
520 unreachable();
521 // GCOVR_EXCL_STOP
522 }
523
524 if (report.sourceLocation)
525 {
526 out = format::format_to(
527 std::move(out),
528 "from: ");
529 out = mimicpp::print(
530 std::move(out),
531 *report.sourceLocation);
532 out = format::format_to(
533 std::move(out),
534 "\n");
535 }
536
537 if (!std::ranges::empty(unmatchedExpectationDescriptions))
538 {
539 out = format::format_to(
540 std::move(out),
541 "failed:\n");
542 for (const auto& desc : unmatchedExpectationDescriptions)
543 {
544 out = format::format_to(
545 std::move(out),
546 "\t{},\n",
547 desc);
548 }
549 }
550
551 if (!std::ranges::empty(matchedExpectationDescriptions))
552 {
553 out = format::format_to(
554 std::move(out),
555 "passed:\n");
556 for (const auto& desc : matchedExpectationDescriptions)
557 {
558 out = format::format_to(
559 std::move(out),
560 "\t{},\n",
561 desc);
562 }
563 }
564
565 return format::format_to(
566 std::move(out),
567 "}}\n");
568 }
569 };
570
574}
575
576#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:222
Constness fromConstness
Definition Reports.hpp:219
ValueCategory fromCategory
Definition Reports.hpp:218
std::vector< Arg > argDetails
Definition Reports.hpp:216
std::source_location fromLoc
Definition Reports.hpp:217
Contains the extracted info from a typed expectation.
Definition Reports.hpp:316
std::optional< StringT > finalizerDescription
Definition Reports.hpp:319
std::optional< StringT > timesDescription
Definition Reports.hpp:320
std::optional< std::source_location > sourceLocation
Definition Reports.hpp:318
std::vector< std::optional< StringT > > expectationDescriptions
Definition Reports.hpp:321
friend bool operator==(const ExpectationReport &lhs, const ExpectationReport &rhs)
Definition Reports.hpp:324
Information a used expectation policy.
Definition Reports.hpp:421
std::optional< StringT > description
Definition Reports.hpp:424
friend bool operator==(const Expectation &, const Expectation &)=default
bool isMatching
Definition Reports.hpp:423
Information about the used finalizer.
Definition Reports.hpp:408
std::optional< StringT > description
Definition Reports.hpp:410
friend bool operator==(const Finalize &, const Finalize &)=default
Contains the detailed information for match outcomes.
Definition Reports.hpp:402
std::vector< Expectation > expectationReports
Definition Reports.hpp:433
Finalize finalizeReport
Definition Reports.hpp:431
friend bool operator==(const MatchReport &lhs, const MatchReport &rhs)
Definition Reports.hpp:436
control_state_t controlReport
Definition Reports.hpp:432
std::optional< std::source_location > sourceLocation
Definition Reports.hpp:430
Definition Fwd.hpp:17
ValueCategory fromCategory
Definition Call.hpp:46
std::source_location fromSourceLocation
Definition Call.hpp:48
ArgListT args
Definition Call.hpp:45
Constness fromConstness
Definition Call.hpp:47
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
CallReport make_call_report(const call::Info< Return, Params... > &callInfo)
Generates the call report for a given call info.
Definition Reports.hpp:242
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:593
Definition BoostTest.hpp:20
MatchResult
Definition Fwd.hpp:330
void unreachable()
Invokes undefined behavior.
Definition Utility.hpp:89
constexpr bool is_same_source_location(const std::source_location &lhs, const std::source_location &rhs) noexcept
Definition Utility.hpp:40
ValueCategory
Definition Fwd.hpp:30
Constness
Definition Fwd.hpp:23
std::basic_string< CharT, CharTraitsT > StringT
Definition Fwd.hpp:342
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