mimic++ v2
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 {
85 switch (value)
86 {
87 case 0: return format::format_to(std::move(o), "never");
88 case 1: return format::format_to(std::move(o), "once");
89 case 2: return format::format_to(std::move(o), "twice");
90 default: return format::format_to(std::move(o), "{} times", value);
91 }
92 };
93
94 if (current == max)
95 {
96 out = format::format_to(
97 std::move(out),
98 "already saturated (matched ");
99 out = verbalizeValue(std::move(out), current);
100 return format::format_to(std::move(out),")");
101 }
102
103 if (min <= current)
104 {
105 return format::format_to(
106 std::move(out),
107 "accepts further matches (matched {} out of {} times)",
108 current,
109 max);
110 }
111
112 const auto verbalizeInterval = [verbalizeValue](OutIter o, const std::size_t start, const std::size_t end)
113 {
114 if (start < end)
115 {
116 return format::format_to(
117 std::move(o),
118 "between {} and {} times",
119 start,
120 end);
121 }
122
123 o = format::format_to(std::move(o), "exactly ");
124 return verbalizeValue(std::move(o), end);
125 };
126
127 out = format::format_to(std::move(out), "matched ");
128 out = verbalizeValue(std::move(out), current);
129 out = format::format_to(std::move(out), " - ");
130 out = verbalizeInterval(std::move(out), min, max);
131 return format::format_to(std::move(out), " is expected");
132 }
133
134 struct control_state_printer
135 {
136 template <print_iterator OutIter>
137 OutIter operator ()(OutIter out, const state_applicable& state) const
138 {
139 out = print_times_state(
140 std::move(out),
141 state.count,
142 state.min,
143 state.max);
144
145 if (const auto sequenceCount = std::ranges::ssize(state.sequenceRatings);
146 0 < sequenceCount)
147 {
148 out = format::format_to(
149 std::move(out),
150 ",\n\tand is the current element of {} sequence(s).",
151 sequenceCount);
152 }
153
154 return out;
155 }
156
157 template <print_iterator OutIter>
158 OutIter operator ()(OutIter out, const state_inapplicable& state) const
159 {
160 out = print_times_state(
161 std::move(out),
162 state.count,
163 state.min,
164 state.max);
165
166 const auto totalSequences = std::ranges::ssize(state.sequenceRatings)
167 + std::ranges::ssize(state.inapplicableSequences);
168 return format::format_to(
169 std::move(out),
170 ",\n\tbut is not the current element of {} sequence(s) ({} total).",
171 std::ranges::ssize(state.inapplicableSequences),
172 totalSequences);
173 }
174
175 template <print_iterator OutIter>
176 OutIter operator ()(OutIter out, const state_saturated& state) const
177 {
178 out = print_times_state(
179 std::move(out),
180 state.count,
181 state.min,
182 state.max);
183
184 if (const auto sequenceCount = std::ranges::ssize(state.sequences);
185 0 < sequenceCount)
186 {
187 out = format::format_to(
188 std::move(out),
189 ",\n\tand is part of {} sequence(s).",
190 sequenceCount);
191 }
192
193 return out;
194 }
195 };
196 }
197
205 {
206 public:
207 class Arg
208 {
209 public:
210 std::type_index typeIndex;
212
213 [[nodiscard]]
214 friend bool operator ==(const Arg&, const Arg&) = default;
215 };
216
217 std::type_index returnTypeIndex;
218 std::vector<Arg> argDetails{};
219 std::source_location fromLoc{};
222
223 [[nodiscard]]
224 friend bool operator ==(const CallReport& lhs, const CallReport& rhs)
225 {
226 return lhs.returnTypeIndex == rhs.returnTypeIndex
227 && lhs.argDetails == rhs.argDetails
229 && lhs.fromCategory == rhs.fromCategory
230 && lhs.fromConstness == rhs.fromConstness;
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 {
251 return std::vector<CallReport::Arg>{
253 .typeIndex = typeid(Params),
254 .stateString = mimicpp::print(args.get())
255 }...
256 };
257 },
258 callInfo.args),
259 .fromLoc = callInfo.fromSourceLocation,
260 .fromCategory = callInfo.fromCategory,
261 .fromConstness = callInfo.fromConstness
262 };
263 }
264
265 template <>
266 class detail::Printer<CallReport>
267 {
268 public:
269 template <print_iterator OutIter>
270 static OutIter print(OutIter out, const CallReport& report)
271 {
272 out = format::format_to(
273 std::move(out),
274 "call from ");
275 out = mimicpp::print(
276 std::move(out),
277 report.fromLoc);
278 out = format::format_to(
279 std::move(out),
280 "\n");
281
282 out = format::format_to(
283 std::move(out),
284 "constness: {}\n"
285 "value category: {}\n"
286 "return type: {}\n",
287 report.fromConstness,
288 report.fromCategory,
289 report.returnTypeIndex.name());
290
291 if (!std::ranges::empty(report.argDetails))
292 {
293 out = format::format_to(
294 std::move(out),
295 "args:\n");
296 for (const std::size_t i : std::views::iota(0u, std::ranges::size(report.argDetails)))
297 {
298 out = format::format_to(
299 std::move(out),
300 "\targ[{}]: {{\n"
301 "\t\ttype: {},\n"
302 "\t\tvalue: {}\n"
303 "\t}},\n",
304 i,
305 report.argDetails[i].typeIndex.name(),
306 report.argDetails[i].stateString);
307 }
308 }
309
310 return out;
311 }
312 };
313
320 {
321 public:
322 std::optional<std::source_location> sourceLocation{};
323 std::optional<StringT> finalizerDescription{};
324 std::optional<StringT> timesDescription{};
325 std::vector<std::optional<StringT>> expectationDescriptions{};
326
327 [[nodiscard]]
328 friend bool operator ==(const ExpectationReport& lhs, const ExpectationReport& rhs)
329 {
333 && lhs.sourceLocation.has_value() == rhs.sourceLocation.has_value()
334 && (!lhs.sourceLocation.has_value()
336 }
337 };
338
339 template <>
340 class detail::Printer<ExpectationReport>
341 {
342 public:
343 template <print_iterator OutIter>
344 static OutIter print(OutIter out, const ExpectationReport& report)
345 {
346 out = format::format_to(
347 std::move(out),
348 "Expectation report:\n");
349
350 if (report.sourceLocation)
351 {
352 out = format::format_to(
353 std::move(out),
354 "from: ");
355 out = mimicpp::print(
356 std::move(out),
357 *report.sourceLocation);
358 out = format::format_to(
359 std::move(out),
360 "\n");
361 }
362
363 if (report.timesDescription)
364 {
365 out = format::format_to(
366 out,
367 "times: {}\n",
368 *report.timesDescription);
369 }
370
371 if (std::ranges::any_of(
372 report.expectationDescriptions,
373 [](const auto& desc) { return desc.has_value(); }))
374 {
375 out = format::format_to(
376 std::move(out),
377 "expects:\n");
378 for (const auto& desc
379 : report.expectationDescriptions
380 | std::views::filter([](const auto& desc) { return desc.has_value(); }))
381 {
382 out = format::format_to(
383 std::move(out),
384 "\t{},\n",
385 *desc);
386 }
387 }
388
389 if (report.finalizerDescription)
390 {
391 out = format::format_to(
392 std::move(out),
393 "finally: {}\n",
394 *report.finalizerDescription);
395 }
396
397 return out;
398 }
399 };
400
407 {
408 public:
413 {
414 public:
415 std::optional<StringT> description{};
416
417 [[nodiscard]]
418 friend bool operator ==(const Finalize&, const Finalize&) = default;
419 };
420
426 {
427 public:
429 std::optional<StringT> description{};
430
431 [[nodiscard]]
432 friend bool operator ==(const Expectation&, const Expectation&) = default;
433 };
434
435 std::optional<std::source_location> sourceLocation{};
438 std::vector<Expectation> expectationReports{};
439
440 [[nodiscard]]
441 friend bool operator ==(const MatchReport& lhs, const MatchReport& rhs)
442 {
443 return lhs.finalizeReport == rhs.finalizeReport
444 && lhs.controlReport == rhs.controlReport
446 && lhs.sourceLocation.has_value() == rhs.sourceLocation.has_value()
447 && (!lhs.sourceLocation.has_value()
449 }
450 };
451
457 [[nodiscard]]
458 inline MatchResult evaluate_match_report(const MatchReport& report) noexcept
459 {
460 if (!std::ranges::all_of(report.expectationReports, &MatchReport::Expectation::isMatching))
461 {
462 return MatchResult::none;
463 }
464
465 if (!std::holds_alternative<state_applicable>(report.controlReport))
466 {
468 }
469
470 return MatchResult::full;
471 }
472
473 template <>
474 class detail::Printer<MatchReport>
475 {
476 public:
477 template <print_iterator OutIter>
478 static OutIter print(OutIter out, const MatchReport& report)
479 {
480 std::vector<StringT> matchedExpectationDescriptions{};
481 std::vector<StringT> unmatchedExpectationDescriptions{};
482
483 for (const auto& [isMatching, description] : report.expectationReports)
484 {
485 if (description)
486 {
487 if (isMatching)
488 {
489 matchedExpectationDescriptions.emplace_back(*description);
490 }
491 else
492 {
493 unmatchedExpectationDescriptions.emplace_back(*description);
494 }
495 }
496 }
497
498 switch (evaluate_match_report(report))
499 {
501 out = format::format_to(
502 std::move(out),
503 "Matched expectation: {{\n");
504 break;
505
507 out = format::format_to(
508 std::move(out),
509 "Inapplicable, but otherwise matched expectation: {{\n"
510 "reason: ");
511 out = std::visit(
512 std::bind_front(control_state_printer{}, std::move(out)),
513 report.controlReport);
514 out = format::format_to(std::move(out), "\n");
515 break;
516
518 out = format::format_to(
519 std::move(out),
520 "Unmatched expectation: {{\n");
521 break;
522
523 // GCOVR_EXCL_START
524 default: // NOLINT(clang-diagnostic-covered-switch-default)
525 unreachable();
526 // GCOVR_EXCL_STOP
527 }
528
529 if (report.sourceLocation)
530 {
531 out = format::format_to(
532 std::move(out),
533 "from: ");
534 out = mimicpp::print(
535 std::move(out),
536 *report.sourceLocation);
537 out = format::format_to(
538 std::move(out),
539 "\n");
540 }
541
542 if (!std::ranges::empty(unmatchedExpectationDescriptions))
543 {
544 out = format::format_to(
545 std::move(out),
546 "failed:\n");
547 for (const auto& desc : unmatchedExpectationDescriptions)
548 {
549 out = format::format_to(
550 std::move(out),
551 "\t{},\n",
552 desc);
553 }
554 }
555
556 if (!std::ranges::empty(matchedExpectationDescriptions))
557 {
558 out = format::format_to(
559 std::move(out),
560 "passed:\n");
561 for (const auto& desc : matchedExpectationDescriptions)
562 {
563 out = format::format_to(
564 std::move(out),
565 "\t{},\n",
566 desc);
567 }
568 }
569
570 return format::format_to(
571 std::move(out),
572 "}}\n");
573 }
574 };
575
579}
580
581#endif
Definition Reports.hpp:208
std::type_index typeIndex
Definition Reports.hpp:210
friend bool operator==(const Arg &, const Arg &)=default
StringT stateString
Definition Reports.hpp:211
Contains the extracted info from a typed call::Info.
Definition Reports.hpp:205
std::type_index returnTypeIndex
Definition Reports.hpp:217
friend bool operator==(const CallReport &lhs, const CallReport &rhs)
Definition Reports.hpp:224
Constness fromConstness
Definition Reports.hpp:221
ValueCategory fromCategory
Definition Reports.hpp:220
std::vector< Arg > argDetails
Definition Reports.hpp:218
std::source_location fromLoc
Definition Reports.hpp:219
Contains the extracted info from a typed expectation.
Definition Reports.hpp:320
std::optional< StringT > finalizerDescription
Definition Reports.hpp:323
std::optional< StringT > timesDescription
Definition Reports.hpp:324
std::optional< std::source_location > sourceLocation
Definition Reports.hpp:322
std::vector< std::optional< StringT > > expectationDescriptions
Definition Reports.hpp:325
friend bool operator==(const ExpectationReport &lhs, const ExpectationReport &rhs)
Definition Reports.hpp:328
Information a used expectation policy.
Definition Reports.hpp:426
std::optional< StringT > description
Definition Reports.hpp:429
friend bool operator==(const Expectation &, const Expectation &)=default
bool isMatching
Definition Reports.hpp:428
Information about the used finalizer.
Definition Reports.hpp:413
std::optional< StringT > description
Definition Reports.hpp:415
friend bool operator==(const Finalize &, const Finalize &)=default
Contains the detailed information for match outcomes.
Definition Reports.hpp:407
std::vector< Expectation > expectationReports
Definition Reports.hpp:438
Finalize finalizeReport
Definition Reports.hpp:436
friend bool operator==(const MatchReport &lhs, const MatchReport &rhs)
Definition Reports.hpp:441
control_state_t controlReport
Definition Reports.hpp:437
std::optional< std::source_location > sourceLocation
Definition Reports.hpp:435
Definition Fwd.hpp:16
ValueCategory fromCategory
Definition Call.hpp:47
std::source_location fromSourceLocation
Definition Call.hpp:49
ArgListT args
Definition Call.hpp:46
Constness fromConstness
Definition Call.hpp:48
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
CallReport make_call_report(const 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:537
Definition BoostTest.hpp:20
MatchResult
Definition Fwd.hpp:86
void unreachable()
Invokes undefined behavior.
Definition Utility.hpp:93
constexpr bool is_same_source_location(const std::source_location &lhs, const std::source_location &rhs) noexcept
Definition Utility.hpp:41
ValueCategory
Definition Fwd.hpp:100
Constness
Definition Fwd.hpp:93
std::basic_string< CharT, CharTraitsT > StringT
Definition Fwd.hpp:112
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