Skip to content

Commit

Permalink
MEMBER_IS
Browse files Browse the repository at this point in the history
  • Loading branch information
rollbear committed Sep 28, 2024
1 parent ccca1d7 commit 167e96b
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 0 deletions.
3 changes: 3 additions & 0 deletions include/trompeloeil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
#include "trompeloeil/matcher/any.hpp"
#include "trompeloeil/matcher/compare.hpp"
#include "trompeloeil/matcher/deref.hpp"
#if TROMPELOEIL_CPLUSPLUS >= 201402L
#include "trompeloeil/matcher/member_is.hpp"
#endif
#include "trompeloeil/matcher/not.hpp"
#if TROMPELOEIL_CPLUSPLUS >= 201402L
#include "trompeloeil/matcher/range.hpp"
Expand Down
61 changes: 61 additions & 0 deletions include/trompeloeil/matcher/member_is.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Trompeloeil C++ mocking framework
*
* Copyright (C) Björn Fahller
* Copyright (C) Andrew Paxie
*
* Use, modification and distribution is subject to the
* Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy atl
* http://www.boost.org/LICENSE_1_0.txt)
*
* Project home: https://github.com/rollbear/trompeloeil
*/

#ifndef TROMPELOEIL_MEMBER_IS_HPP
#define TROMPELOEIL_MEMBER_IS_HPP

#ifndef TROMPELOEIL_MATCHER_HPP
#include "../matcher.hpp"
#endif

namespace trompeloeil
{

namespace impl {
template <typename M>
struct member_is_matcher
{
template <typename V, typename C>
bool operator()(const V& v, const C& c) const
{
return trompeloeil::param_matches(c, std::ref(m(v)));
}
M m;
};
}

template <typename M, typename C>
auto match_member_is(M m, const char* name, C&& c)
{
return trompeloeil::make_matcher<trompeloeil::wildcard>(
impl::member_is_matcher<M>{m},
[name](std::ostream& os, const C& compare) {
os << ' ' << name << compare;
},
std::forward<C>(c)
);
}
}

#define TROMPELOEIL_MEMBER_IS(member_name, compare) \
trompeloeil::match_member_is( \
std::mem_fn(member_name), \
#member_name, \
compare)

#ifndef TROMPELOEIL_LONG_MACROS
#define MEMBER_IS TROMPELOEIL_MEMBER_IS
#endif

#endif // TROMPELOEIL_MEMBER_IS_HPP
105 changes: 105 additions & 0 deletions test/compiling_tests_14.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4331,6 +4331,109 @@ TEST_CASE_METHOD(
REQUIRE(reports.empty());
}

struct xy_coord {
int x;
int y;
friend std::ostream& operator<<(std::ostream& os, xy_coord c)
{
return os << "{ .x=" << c.x << ", .y=" << c.y << " }";
}
};
struct xy_mock {
MAKE_MOCK1(func, void(const xy_coord&));
MAKE_MOCK1(vfunc, void(const std::vector<xy_coord>&));
};


TEST_CASE_METHOD(
Fixture,
"C++14: MEMBER_IS can match a public member of a struct",
"[C++14][matching][matchers][MEMBER_IS]"
)
{
{
xy_mock m;
using trompeloeil::eq;
REQUIRE_CALL(m, func(MEMBER_IS(&xy_coord::x, eq(3))));
m.func({3, 2});
}
REQUIRE(reports.empty());
}

TEST_CASE_METHOD(
Fixture,
"C++14: MEMBER_IS mismatching is reported",
"[C++14][matching][matchers][MEMBER_IS]"
)
{
try {
xy_mock m;
using trompeloeil::eq;
REQUIRE_CALL(m, func(MEMBER_IS(&xy_coord::x, eq(3))));
m.func({1, 2});
FAIL("didn't throw");
}
catch (reported) {
REQUIRE(reports.size() == 1U);
INFO("report=" << reports.front().msg);
auto re = R":(No match for call of func with signature void\(const xy_coord&\) with\.
param _1 == \{ \.x=1, \.y=2 \}
Tried m\.func\(MEMBER_IS\(&xy_coord::x, eq\(3\)\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.*
Expected _1 &xy_coord::x == 3):";
REQUIRE(std::regex_search(reports.front().msg, std::regex(re)));
}
}

TEST_CASE_METHOD(
Fixture,
"C++14: MEMBER_IS, range_is_all and any_of can be composed",
"[C++14][matching][matchers][MEMBER_IS][range_is_all][any_of]"
)
{
{
xy_mock m;
using trompeloeil::lt;
using trompeloeil::gt;
using trompeloeil::any_of;
using trompeloeil::range_is_all;
REQUIRE_CALL(m, vfunc(range_is_all(any_of(MEMBER_IS(&xy_coord::x, gt(0)), MEMBER_IS(&xy_coord::y, lt(0))))));
m.vfunc({{1,1},{0,-1},{-1,-1}});
}
REQUIRE(reports.empty());
}

TEST_CASE_METHOD(
Fixture,
"C++14: A failed composition of MEMBER_IS, range_is_all and any_of is reported",
"[C++14][matching][matchers][MEMBER_IS][range_is_all][any_of]"
)
{
try {
xy_mock m;
using trompeloeil::lt;
using trompeloeil::gt;
using trompeloeil::any_of;
using trompeloeil::range_is_all;
REQUIRE_CALL(m, vfunc(range_is_all(any_of(MEMBER_IS(&xy_coord::x, gt(0)), MEMBER_IS(&xy_coord::y, lt(0))))));
m.vfunc({{1,1},{0,-1},{-1,1}});
FAIL("didn't throw");
}
catch (reported)
{
REQUIRE(reports.size() == 1U);
auto re = R":(No match for call of vfunc with signature void\(const std::vector<xy_coord>&\) with\.
param _1 == \{ .* \}
Tried m\.vfunc\(range_is_all\(any_of\(MEMBER_IS\(&xy_coord::x, gt\(0\)\), MEMBER_IS\(&xy_coord::y, lt\(0\)\)\)\)\) at [A-Za-z0-9_ ./:\]*:[0-9]*.*
Expected _1 range is all to be any of \{ &xy_coord::x > 0, &xy_coord::y < 0 \}):";
auto& msg = reports.front().msg;
INFO("msg=" << msg);
REQUIRE(std::regex_search(msg, std::regex(re)));

}
}

#if TROMPELOEIL_TEST_REGEX_FAILURES

TEST_CASE_METHOD(
Expand Down Expand Up @@ -4358,6 +4461,8 @@ Tried obj\.foo\(not_empty\{\}\) at [A-Za-z0-9_ ./:\]*:[0-9]*.*
}
}



#endif /* TROMPELOEIL_TEST_REGEX_FAILURES */

#if !(TROMPELOEIL_CLANG && \
Expand Down

0 comments on commit 167e96b

Please sign in to comment.