Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Policies are allowed to return no description #108

Merged
merged 8 commits into from
Mar 17, 2025
11 changes: 6 additions & 5 deletions include/mimic++/Expectation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "mimic++/reporting/ExpectationReport.hpp"
#include "mimic++/reporting/GlobalReporter.hpp"
#include "mimic++/reporting/TargetReport.hpp"
#include "mimic++/utilities/Concepts.hpp"
#include "mimic++/utilities/SourceLocation.hpp"

#include <algorithm>
Expand Down Expand Up @@ -428,9 +429,9 @@ namespace mimicpp
&& std::is_destructible_v<T>
&& std::same_as<T, std::remove_cvref_t<T>>
&& requires(T& policy, const call::info_for_signature_t<Signature>& info) {
{ std::as_const(policy).is_satisfied() } noexcept -> std::convertible_to<bool>;
{ std::as_const(policy).matches(info) } -> std::convertible_to<bool>;
{ std::as_const(policy).describe() } -> std::convertible_to<std::optional<StringT>>;
{ std::as_const(policy).is_satisfied() } noexcept -> util::boolean_testable;
{ std::as_const(policy).matches(info) } -> util::boolean_testable;
{ std::as_const(policy).describe() } -> util::explicitly_convertible_to<std::optional<StringT>>;
{ policy.consume(info) };
};

Expand All @@ -453,7 +454,7 @@ namespace mimicpp
&& std::is_destructible_v<T>
&& std::same_as<T, std::remove_cvref_t<T>>
&& requires(T& policy) {
{ std::as_const(policy).is_satisfied() } noexcept -> std::convertible_to<bool>;
{ std::as_const(policy).is_satisfied() } noexcept -> util::boolean_testable;
{ std::as_const(policy).state() } -> std::convertible_to<reporting::control_state_t>;
policy.consume();
};
Expand Down Expand Up @@ -527,7 +528,7 @@ namespace mimicpp
.requirementDescriptions = std::apply(
[&](auto const&... policies) {
return std::vector<std::optional<StringT>>{
policies.describe()...};
std::optional<StringT>{policies.describe()}...};
},
m_Policies)};
}
Expand Down
18 changes: 10 additions & 8 deletions include/mimic++/matchers/Common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
#pragma once

#include "mimic++/Fwd.hpp"
#include "mimic++/utilities/Concepts.hpp"
#include "mimic++/utilities/PriorityTag.hpp"

#include <concepts>
#include <optional>
#include <type_traits>

namespace mimicpp::custom
Expand All @@ -32,7 +34,7 @@ namespace mimicpp::detail::matches_hook
requires requires {
{
custom::matcher_traits<Matcher>{}.matches(matcher, target, others...)
} -> std::convertible_to<bool>;
} -> util::boolean_testable;
}
{
return custom::matcher_traits<Matcher>{}.matches(matcher, target, others...);
Expand All @@ -45,7 +47,7 @@ namespace mimicpp::detail::matches_hook
Matcher const& matcher,
T& target,
Others&... others)
requires requires { { matcher.matches(target, others...) } -> std::convertible_to<bool>; }
requires requires { { matcher.matches(target, others...) } -> util::boolean_testable; }
{
return matcher.matches(target, others...);
}
Expand All @@ -59,7 +61,7 @@ namespace mimicpp::detail::matches_hook
requires requires {
{
matches_impl(maxTag, matcher, target, others...)
}-> std::convertible_to<bool>; }
} -> util::boolean_testable; }
{
return matches_impl(maxTag, matcher, target, others...);
};
Expand All @@ -75,7 +77,7 @@ namespace mimicpp::detail::describe_hook
requires requires {
{
custom::matcher_traits<Matcher>{}.describe(matcher)
} -> std::convertible_to<StringViewT>;
} -> util::explicitly_convertible_to<std::optional<StringT>>;
}
{
return custom::matcher_traits<Matcher>{}.describe(matcher);
Expand All @@ -86,15 +88,15 @@ namespace mimicpp::detail::describe_hook
constexpr decltype(auto) describe_impl(
[[maybe_unused]] util::priority_tag<0> const,
Matcher const& matcher)
requires requires { { matcher.describe() } -> std::convertible_to<StringViewT>; }
requires requires { { matcher.describe() } -> util::explicitly_convertible_to<std::optional<StringT>>; }
{
return matcher.describe();
}

constexpr util::priority_tag<1> maxTag{};

constexpr auto describe = []<typename Matcher>(Matcher const& matcher) -> decltype(auto)
requires requires { { describe_impl(maxTag, matcher) } -> std::convertible_to<StringViewT>; }
requires requires { { describe_impl(maxTag, matcher) } -> util::explicitly_convertible_to<std::optional<StringT>>; }
{
return describe_impl(maxTag, matcher);
};
Expand All @@ -107,8 +109,8 @@ namespace mimicpp
&& std::is_move_constructible_v<T>
&& std::destructible<T>
&& requires(T const& matcher, First& first, Others&... others) {
{ detail::matches_hook::matches(matcher, first, others...) } -> std::convertible_to<bool>;
{ detail::describe_hook::describe(matcher) } -> std::convertible_to<StringViewT>;
{ detail::matches_hook::matches(matcher, first, others...) } -> util::boolean_testable;
{ detail::describe_hook::describe(matcher) } -> util::explicitly_convertible_to<std::optional<StringT>>;
};
}

Expand Down
6 changes: 4 additions & 2 deletions include/mimic++/matchers/GeneralMatchers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,14 +189,16 @@ namespace mimicpp
class WildcardMatcher
{
public:
[[nodiscard]]
static constexpr bool matches([[maybe_unused]] auto&& target) noexcept
{
return true;
}

static constexpr StringViewT describe() noexcept
[[nodiscard]]
static constexpr std::nullopt_t describe() noexcept
{
return "has no constraints";
return std::nullopt;
}
};
}
Expand Down
39 changes: 28 additions & 11 deletions include/mimic++/policies/ArgRequirementPolicies.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "mimic++/matchers/Common.hpp"
#include "mimic++/policies/ArgumentList.hpp"
#include "mimic++/utilities/Concepts.hpp"
#include "mimic++/utilities/TypeList.hpp"

#include <concepts>
Expand All @@ -25,7 +26,7 @@ namespace mimicpp::expectation_policies
struct matcher_matches_fn
{
public:
const Matcher& matcher;
Matcher const& matcher;

template <typename... Args>
requires std::invocable<
Expand All @@ -39,7 +40,7 @@ namespace mimicpp::expectation_policies
noexcept(
std::is_nothrow_invocable_v<
decltype(detail::matches_hook::matches),
const Matcher&,
Matcher const&,
Args&...>)
{
return detail::matches_hook::matches(matcher, args...);
Expand Down Expand Up @@ -72,10 +73,10 @@ namespace mimicpp::expectation_policies
}

template <typename Return, typename... Args>
requires std::is_invocable_r_v<bool, const MatchesStrategy&, matcher_matches_fn<Matcher>, const call::Info<Return, Args...>&>
requires std::is_invocable_r_v<bool, MatchesStrategy const&, matcher_matches_fn<Matcher>, call::Info<Return, Args...> const&>
[[nodiscard]]
constexpr bool matches(const call::Info<Return, Args...>& info) const
noexcept(std::is_nothrow_invocable_v<const MatchesStrategy&, matcher_matches_fn<Matcher>, const call::Info<Return, Args...>&>)
noexcept(std::is_nothrow_invocable_v<MatchesStrategy const&, matcher_matches_fn<Matcher>, call::Info<Return, Args...> const&>)
{
return std::invoke(
m_MatchesStrategy,
Expand All @@ -84,16 +85,32 @@ namespace mimicpp::expectation_policies
}

template <typename Return, typename... Args>
static constexpr void consume([[maybe_unused]] const call::Info<Return, Args...>& info) noexcept
static constexpr void consume([[maybe_unused]] call::Info<Return, Args...> const& info) noexcept
{
}

[[nodiscard]]
StringT describe() const
std::optional<StringT> describe() const
{
return std::invoke(
m_DescribeStrategy,
detail::describe_hook::describe(m_Matcher));
[[maybe_unused]] auto const description = detail::describe_hook::describe(m_Matcher);

if constexpr (util::boolean_testable<decltype(description)>)
{
if (description)
{
return std::invoke(m_DescribeStrategy, *description);
}

return std::nullopt;
}
else if constexpr (std::convertible_to<decltype(description), StringViewT>)
{
return std::invoke(m_DescribeStrategy, description);
}
else
{
return std::nullopt;
}
}

private:
Expand All @@ -111,7 +128,7 @@ namespace mimicpp::expect
struct arg_requirement_describer
{
[[nodiscard]]
StringT operator()(const StringViewT matcherDescription) const
StringT operator()(StringViewT const matcherDescription) const
{
StringStreamT out{};
out << "expect: arg[" << index;
Expand All @@ -124,7 +141,7 @@ namespace mimicpp::expect
struct all_args_requirement_describer
{
[[nodiscard]]
StringT operator()(const StringViewT matcherDescription) const
StringT operator()(StringViewT const matcherDescription) const
{
StringStreamT out{};
out << "expect: arg[all] " << matcherDescription;
Expand Down
47 changes: 33 additions & 14 deletions include/mimic++/policies/GeneralPolicies.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "mimic++/Fwd.hpp"
#include "mimic++/config/Config.hpp"
#include "mimic++/printing/StatePrinter.hpp"
#include "mimic++/utilities/C++23Backports.hpp"

#include <iterator>

Expand Down Expand Up @@ -44,12 +45,14 @@ namespace mimicpp::expectation_policies
class Category
{
public:
[[nodiscard]]
static constexpr bool is_satisfied() noexcept
{
return true;
}

template <typename Return, typename... Args>
[[nodiscard]]
static constexpr bool matches(const call::Info<Return, Args...>& info) noexcept
{
return mimicpp::detail::is_matching(info.fromCategory, expected);
Expand All @@ -62,27 +65,36 @@ namespace mimicpp::expectation_policies
}

[[nodiscard]]
static StringT describe()
static auto describe()
{
StringStreamT stream{};
stream << "expect: from ";
mimicpp::print(std::ostreambuf_iterator{stream}, expected);
stream << " category overload";

return std::move(stream).str();
if constexpr (ValueCategory::any != expected)
{
StringStreamT stream{};
stream << "expect: from ";
mimicpp::print(std::ostreambuf_iterator{stream}, expected);
stream << " category overload";

return std::move(stream).str();
}
else
{
return std::nullopt;
}
}
};

template <Constness constness>
class Constness
{
public:
[[nodiscard]]
static constexpr bool is_satisfied() noexcept
{
return true;
}

template <typename Return, typename... Args>
[[nodiscard]]
static constexpr bool matches(const call::Info<Return, Args...>& info) noexcept
{
return mimicpp::detail::is_matching(info.fromConstness, constness);
Expand All @@ -95,14 +107,21 @@ namespace mimicpp::expectation_policies
}

[[nodiscard]]
static StringT describe()
static auto describe()
{
StringStreamT stream{};
stream << "expect: from ";
mimicpp::print(std::ostreambuf_iterator{stream}, constness);
stream << " qualified overload";

return std::move(stream).str();
if constexpr (mimicpp::Constness::any != constness)
{
StringStreamT stream{};
stream << "expect: from ";
mimicpp::print(std::ostreambuf_iterator{stream}, constness);
stream << " qualified overload";

return std::move(stream).str();
}
else
{
return std::nullopt;
}
}
};
}
Expand Down
48 changes: 48 additions & 0 deletions test/unit-tests/Expectation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,54 @@ TEST_CASE(
*report.requirementDescriptions.front(),
Matches::Equals("expectation description"));
}

SECTION("Expectation policies without a description are supported.")
{
struct Policy
{
[[nodiscard]]
static constexpr bool is_satisfied() noexcept
{
return true;
}

[[nodiscard]]
static constexpr bool matches([[maybe_unused]] call::info_for_signature_t<void()> const& call) noexcept
{
return true;
}

[[nodiscard]]
static constexpr std::nullopt_t describe() noexcept
{
return std::nullopt;
}

static constexpr void consume([[maybe_unused]] call::info_for_signature_t<void()> const& call) noexcept
{
}
};

static_assert(expectation_policy_for<Policy, void()>);

BasicExpectation<
void(),
ControlPolicyFake,
FinalizerPolicyT,
Policy>
expectation{
{},
make_common_target_report<void()>(),
ControlPolicyFake{},
FinalizerPolicyT{},
Policy{}};

reporting::ExpectationReport const report = expectation.report();
REQUIRE_THAT(
report.requirementDescriptions,
Matches::SizeIs(1));
REQUIRE_FALSE(report.requirementDescriptions.front());
}
}

TEMPLATE_TEST_CASE(
Expand Down
Loading
Loading