Skip to content

Commit

Permalink
Added range matchers
Browse files Browse the repository at this point in the history
  • Loading branch information
rollbear committed Sep 22, 2024
1 parent d9f47c1 commit 63a54b4
Show file tree
Hide file tree
Showing 6 changed files with 900 additions and 0 deletions.
8 changes: 8 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
* Added range matchers range_is(*elements*), range_is_all(*comparator*),
range_is_none(*comparator*), range_starts_with(*elements*) and
range_ends_with(*elements*). Use in expectations as:

REQUIRE_CALL(obj, vector_func(range_starts_with(2,3,5,8)));

Thank you Artem for the suggestion

v48 2024-07-17

* Fixed memory leak when adding an action (.RETURN, .SIDE_EFFECT, etc)
Expand Down
39 changes: 39 additions & 0 deletions docs/CookBook.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- [Setting Expectations](#setting_expectations)
- [Matching exact values](#matching_exact_values)
- [Matching values with conditions](#matching_conditions)
- [Matching ranges with conditions](#matching_ranges)
- [Matching strings with regular expressions](#matching_regular_expressions)
- [Matching pointers to values](#matching_pointers)
- [Matching the opposite of a matcher](#negating_matchers)
Expand Down Expand Up @@ -1057,6 +1058,44 @@ void test()
}
```

### <A name="matching_ranges"/> Matching ranges with conditions

Instead of using exact values of parameters to match calls with, *Trompeloeil*
provides a set of [matchers](reference.md/#matcher). Range matchers are:

- [**`range_is(`** *range* **`)`**](reference.md/#range_is) matches values of each element in the range with expected values
- [**`range_starts_with(`** *range* **`)`**](reference.md/#range_starts_with) matches values of the first elements in the range with expected values
- [**`range_ends_with(`** *range* **`)`**](reference.md/#range_ends_with) matches values of the last elements in the range with expected values
- [**`range_is_all(`** *value* **`)`**](reference.md/#range_is_all) matches when every element in the range matches value
- [**`range_is_none(`** *value* **`)`**](reference.md/#range_is_none) matches when no element in the range matches value

By default, the matchers are [*duck typed*](
https://en.wikipedia.org/wiki/Duck_typing
), i.e. they match a parameter that supports the operation. If disambiguation
is necessary to resolve overloads, an explicit type can be specified.

Example:

```Cpp
class Mock
{
public:
MAKE_MOCK1(vfunc, void(const std::vector<int>&));
MAKE_MOCK1(ofunc, void(const std::vector<int>&));
MAKE_MOCK1(ofunc, void(const std::list<int>&))
};

void test()
{
Mock m;
ALLOW_CALL(m, vfunc(trompeloeil::range_starts_with(1,2,3)));
REQUIRE_CALL(m, ofunc(trompeloeil::range_is_all<std::vector<int>>(trompeloeil::ge(0)))); // const std::vector<int>& version once
func(&m);
// expectations must be met before end of scope
}
```
### <A name="matching_regular_expressions"/> Matching strings with regular expressions
Matching string parameters to regular expressions is convenient with
Expand Down
217 changes: 217 additions & 0 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
- [**`lt(`** *value* **`)`**](#lt)
- [**`le(`** *value* **`)`**](#le)
- [**`re(`** *string* **`)`**](#re)
- [**`range_is(`** *range* **`)`**](#range_is)
- [**`range_is_all(`** *value* **`)`**](#range_is_all)
- [**`range_is_none(`** *value* **`)`**](#range_is_none)
- [**`range_starts_with(`** *range* **`)`**](#range_starts_with)
- [**`range_ends_with(`** *range* **`)`**](#range_ends_with)
- [**`*`** *matcher*](#deref_matcher)
- [**`!`** *matcher*](#not_matcher)
- [Macros](#macros) (alphabetical order)
Expand Down Expand Up @@ -608,6 +613,218 @@ It is also possible to use `*re(string)` to match a pointer to a string with
a regular expression, or `!re(string)` to allow only strings that do not match
a regular expression.
#### <A name="range_is"/>**`range_is`** *matcher*
Used in the parameter list of an [expectation](#expectation) to match a
range with a set of matchers. By default it can match any range-like
type, but it can be explicitly disambiguated by providing a type to match for.
`#include <trompeloeil/matcher/range.hpp>`
Example:
```Cpp
class C
{
public:
MAKE_MOCK1(vfunc, void(const std::vector<int>&));
MAKE_MOCK1(ofunc, void(const std::vector<int>&));
MAKE_MOCK1(ofunc, void(const std::list<int>&));
};
using trompeloeil::le;
using trompeloeil::range_is;
TEST(atest)
{
C mock_obj;
ALLOW_CALL(mock_obj, vfunc(range_is(1, 3, le(0))));
REQUIRE_CALL(mock_obj, ofunc(range_is<std::list<int>>(1,2,3)));
test_function(&mock_obj);
}
```

Above, first the [expectation](#expectation) matches only calls to
`mock_obj.vfunc(const std::vector<int>&)` with the values `{ 1, 3, x }`,
where `x <= 0`. Any call with a different set of values
renders a violation report since no [expectation](#expectation) matches.

The second [expectation](#expectation) matches calls to
`mock_obj.ofunc(const std::list<int>&)` with the values `{ 1, 2, 3 }`.


#### <A name="range_is_all"/>**`range_is_all`** *matcher*

Used in the parameter list of an [expectation](#expectation) to match each
element in a range to a matcher or value. By default it can match any
range-like type, but it can be explicitly disambiguated by providing a
type to match for.

`#include <trompeloeil/matcher/range.hpp>`

Example:

```Cpp
class C
{
public:
MAKE_MOCK1(vfunc, void(const std::vector<int>&));
MAKE_MOCK1(ofunc, void(const std::vector<int>&));
MAKE_MOCK1(ofunc, void(const std::list<int>&));
};

using trompeloeil::gt;
using trompeloeil::range_is_all;

TEST(atest)
{
C mock_obj;
ALLOW_CALL(mock_obj, vfunc(range_is_all(gt(0))));
REQUIRE_CALL(mock_obj, ofunc(range_is_all<std::list<int>>(gt(0))));
test_function(&mock_obj);
}
```
Above, first the [expectation](#expectation) matches only calls to
`mock_obj.vfunc(const std::vector<int>&)` with values `> 0`,
Any call with a different set of values
renders a violation report since no [expectation](#expectation) matches.
The second [expectation](#expectation) matches calls to
`mock_obj.ofunc(const std::list<int>&)` with values `> 0`.
The matcher `range_is_all` does never match an empty range.
#### <A name="range_is_none"/>**`range_is_none`** *matcher*
Used in the parameter list of an [expectation](#expectation) to match each
element in a range to a matcher or value. By default it can match any
range-like type, but it can be explicitly disambiguated by providing a
type to match for.
`#include <trompeloeil/matcher/range.hpp>`
Example:
```Cpp
class C
{
public:
MAKE_MOCK1(vfunc, void(const std::vector<int>&));
MAKE_MOCK1(ofunc, void(const std::vector<int>&));
MAKE_MOCK1(ofunc, void(const std::list<int>&));
};
using trompeloeil::lt;
using trompeloeil::range_is_none;
TEST(atest)
{
C mock_obj;
ALLOW_CALL(mock_obj, vfunc(range_is_none(0)));
REQUIRE_CALL(mock_obj, ofunc(range_is_none<std::list<int>>(lt(0))));
test_function(&mock_obj);
}
```

Above, first the [expectation](#expectation) matches only calls to
`mock_obj.vfunc(const std::vector<int>&)` where none of the values are `0`,
Any call with a at least one value `== 0` renders a violation report since
no [expectation](#expectation) matches.

The second [expectation](#expectation) matches calls to
`mock_obj.ofunc(const std::list<int>&)` where none of the values are `< 0`.

The matcher `range_is_nonel` always matches an empty range.


#### <A name="range_starts_with"/>**`range_starts_with`** *matcher*

Used in the parameter list of an [expectation](#expectation) to match the
first values in a range to a set of matchers. By default it can match any
range-like type, but it can be explicitly disambiguated by providing a type
to match for.

`#include <trompeloeil/matcher/range.hpp>`

Example:

```Cpp
class C
{
public:
MAKE_MOCK1(vfunc, void(const std::vector<int>&));
MAKE_MOCK1(ofunc, void(const std::vector<int>&));
MAKE_MOCK1(ofunc, void(const std::list<int>&));
};

using trompeloeil::le;
using trompeloeil::range_starts_with;

TEST(atest)
{
C mock_obj;
ALLOW_CALL(mock_obj, vfunc(range_starts_with(1, le(0))));
REQUIRE_CALL(mock_obj, ofunc(range_starts_with<std::list<int>>(1,2)));
test_function(&mock_obj);
}
```
Above, first the [expectation](#expectation) matches only calls to
`mock_obj.vfunc(const std::vector<int>&)` with a vector holding at
least two elements, starting with `{ 1, x, }`, where `x <= 0`.
Any call with a different set of values renders a violation report
since no [expectation](#expectation) matches.
The second [expectation](#expectation) matches calls to
`mock_obj.ofunc(const std::list<int>&)` with at least two values,
starting with `{ 1, 2 }`.
#### <A name="range_ends_with"/>**`range_ends_with`** *matcher*
Used in the parameter list of an [expectation](#expectation) to match the
last values in a range to a set of matchers. By default it can match any
range-like type, but it can be explicitly disambiguated by providing a type
to match for.
`#include <trompeloeil/matcher/range.hpp>`
Example:
```Cpp
class C
{
public:
MAKE_MOCK1(vfunc, void(const std::vector<int>&));
MAKE_MOCK1(ofunc, void(const std::vector<int>&));
MAKE_MOCK1(ofunc, void(const std::list<int>&));
};
using trompeloeil::le;
using trompeloeil::range_ends_with;
TEST(atest)
{
C mock_obj;
ALLOW_CALL(mock_obj, vfunc(range_ends_with(1, le(0))));
REQUIRE_CALL(mock_obj, ofunc(range_ends_with<std::list<int>>(1,2)));
test_function(&mock_obj);
}
```

Above, first the [expectation](#expectation) matches only calls to
`mock_obj.vfunc(const std::vector<int>&)` with a vector holding at
least two elements, ending with `{ 1, x, }`, where `x <= 0`.
Any call with a different set of values renders a violation report
since no [expectation](#expectation) matches.

The second [expectation](#expectation) matches calls to
`mock_obj.ofunc(const std::list<int>&)` with at least two values,
ending with `{ 1, 2 }`.


#### <A name="deref_matcher"/>**`*`** *matcher*

Used in the parameter list of an [expectation](#expectation) together with a
Expand Down
3 changes: 3 additions & 0 deletions include/trompeloeil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
#include "trompeloeil/matcher/compare.hpp"
#include "trompeloeil/matcher/deref.hpp"
#include "trompeloeil/matcher/not.hpp"
#if TROMPELOEIL_CPLUSPLUS >= 201402L
#include "trompeloeil/matcher/range.hpp"
#endif
#include "trompeloeil/matcher/re.hpp"
#include "trompeloeil/sequence.hpp"
#include "trompeloeil/stream_tracer.hpp"
Expand Down
Loading

0 comments on commit 63a54b4

Please sign in to comment.