Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/origin/develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
rollbear committed Jan 21, 2019
2 parents a6b3d18 + 22661e2 commit 17edd13
Show file tree
Hide file tree
Showing 10 changed files with 343 additions and 23 deletions.
21 changes: 21 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
* Silenced noisy g++-7 -Wuseless-cast warning

* Fixed a bug where a mocked function would not compile if a parameter
had an operator==(nullptr) returning a type that is not convertible
to bool.

* Fixed a bug where a mocked function would not compile if a parameter
was a range with iterators to rvalue proxy objects, like vector<bool>.

* Mock objects can be move constructed if they have a static constexpr
bool member named trompeloeil_movable_mock with value = true.

* ANY() matcher gives a short descriptive compilation error message
when the type is an array type (it would silently decay into a
pointer type, which can be very confusing.)

* When compiled with preprocessor macro
TROMPELOEIL_USER_DEFINED_COMPILE_TIME_REPORTER, a `extern`
`trompeloeil::reporter` is declared from `trompeloeil.hpp`.
Thanks @rcdailey

* Minor documentation updates.

* Update pattern in expectation_with_wrong_type.cpp to match
Expand Down
37 changes: 37 additions & 0 deletions compilation_errors/any_with_array_type.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Trompeloeil C++ mocking framework
*
* Copyright Björn Fahller 2018
*
* Use, modification and distribution is subject to the
* Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* Project home: https://github.com/rollbear/trompeloeil
*/

//array parameter type decays to pointer type for ANY

#include <trompeloeil.hpp>


struct MS
{
MAKE_MOCK1(f, void(int [3]));
};

int main()
{
MS obj;

#if (TROMPELOEIL_CPLUSPLUS == 201103L)

REQUIRE_CALL_V(obj, f(ANY(int[3])));

#else /* (TROMPELOEIL_CPLUSPLUS == 201103L) */

REQUIRE_CALL(obj, f(ANY(int[3])));

#endif /* !(TROMPELOEIL_CPLUSPLUS == 201103L) */
}
33 changes: 33 additions & 0 deletions compilation_errors/illegal_move_mock.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Trompeloeil C++ mocking framework
*
* Copyright Björn Fahller 2019
*
* Use, modification and distribution is subject to the
* Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* Project home: https://github.com/rollbear/trompeloeil
*/

// make a mock object movable, see:

#include <trompeloeil.hpp>


struct M
{
MAKE_MOCK1(f, void(int));
};

template <typename T>
T ident(T t)
{
return t;
}

int main()
{
auto obj = ident(M{});
}
14 changes: 11 additions & 3 deletions docs/CookBook.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,22 @@ namespace trompeloeil
}
```

In all other [translation units](
http://stackoverflow.com/questions/8342185/ddg#8342233
), add the following extern declaration in global namespace instead:
If you have multiple translation units, you can define the
`TROMPELOEIL_USER_DEFINED_COMPILE_TIME_REPORTER` preprocessor definition which causes the
`<trompeloeil.hpp>` header to automatically generate the following `extern` statement:

```Cpp
extern template struct trompeloeil::reporter<trompeloeil::specialized>;
```
This preprocessor definition can be added to your unit test code via your build system to make this
more transparent to your code. This definition provides the ability to define a compile-time
reporter without being required to insert code into multiple, existing translation units.
The old (legacy) requirement was that the `extern` statement above had to be explicitly declared in
every translation unit, which is tedious. However, this is still a functional alternative if you
can't define the preprocessor directive at the build system level for whatever reason.
It is important to understand the first parameter
`trompeloeil::severity`. It is an enum with the values
`trompeloeil::severity::fatal` and `trompeloeil::severity::nonfatal`. The value
Expand Down
22 changes: 22 additions & 0 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- Q. [Can I check if an expectation is fulfilled?](#query_expectation)
- Q. [What does it mean to mix **`IN_SEQUENCE`** and **`TIMES`**?](#sequence_times)
- Q. [How do I use *Trompeloeil* in a CMake project?](#cmake)
- Q. [Why are mock objects not move constructible?](#move_constructible)

## <A name="why_name"/>Q. Why a name that can neither be pronounced nor spelled?

Expand Down Expand Up @@ -571,3 +572,24 @@ Finally, you can add *Trompeloeil* to your project and then either (a) use CMake
`include_directories()`; or (b) use `add_subdirectory()` (one or two argument
version) to add its path to your project.


### <A name="move_constructible"/>

Q. Why are mock objects not move constructible?

Because a move is potentially dangerous in non-obvious ways. If a mock object is
moved, the actions associated with an expectation
([**`.WITH()`**](reference.md/#WITH),
[**`.SIDE_EFFECT()`**](reference.md/#SIDE_EFFECT),
[**`.RETURN()`**](reference.md/#RETURN),
[**`.THROW()`**](reference.md/#THROW)) and their
`LR_` versions, are *not* moved. If they refer to data members stored in a
moved mock object, they will refer to dead data. This is an accepted const
in normal C++ code, but since the effect is hidden under the macros,
it is better to play safe.

With that said, you can explicitly make mock objects movable, if you want to.
See: [**`trompeloeil_movable_mock`**](reference.md/#movable_mock).



77 changes: 76 additions & 1 deletion docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
- [`trompeloeil::print(std::ostream&, T const&)`](#print)
- [`trompeloeil::set_reporter(...)`](#set_reporter)
- [`trompeloeil::sequence::is_completed()`](#is_completed)
- [Constants](#constants)
- [`trompeloeil_movable_mock`](#movable_mock)

## <A name="notions"/>Notions

Expand Down Expand Up @@ -2122,14 +2124,15 @@ protected:
See "[Writing custom tracers](CookBook.md/#custom_tracer)" in the
[Cook Book](CookBook.md) for an example.

### <A name="typed_matcher"/>`trompeloeil::typed_matcher<T>`
### <A name="typed_matcher"/> `trompeloeil::typed_matcher<T>`

Convenience class available when writing custom matchers for a specific
type. It inherits from [`trompeloeil::matcher`](#matcher_type).

See "[Writing custom matchers](CookBook.md/#custom_matchers)" in the
[Cook Book](CookBook.md) for examples.


## <A name="functions"/>Functions

### <A name="is_satisfied"/> `trompeloeil::expectation::is_satisfied() const`
Expand Down Expand Up @@ -2277,3 +2280,75 @@ void test()
assert(seq.is_completed()); // now sequence is completed
}
```

## <A name="constants"/>Constants

### <A name="movable_mock"/> `trompeloeil_movable_mock`

By adding a static constexpr bool member `trompeloeil_movable_mock` with the
value `true` to your mock struct/class, you make it move constructible. Note
that when a mock object is moved, any current expectations will be taken over
by the newly constructed mock object, but note also that if the implicitly
created lambdas associated with
[**`.WITH()`**](reference.md/#WITH),
[**`.SIDE_EFFECT()`**](reference.md/#SIDE_EFFECT),
[**`.RETURN()`**](reference.md/#RETURN) and
[**`.THROW()`**](reference.md/#THROW) and their `**LR_**` counter parts, refers
to member variables in the mock objects, they will continue to refer the old
moved from object.

Also, keep in mind the lifetime of expectations. If the lifetime of an
expectation is associated with the life of the moved-from object, your test
will likely fail, since the expectation object would then be destroyed before it
has been satisfied. Using
[**`NAMED_REQUIRE_CALL()`**](reference.md/#NAMED_REQUIRE_CALL),
[**`NAMED_ALLOW_CALL()`**](reference.md/#NAMED_ALLOW_CALL) or
[**`NAMED_FORBID_CALL()`**](reference.md/#NAMED_FORBID_CALL) can help, since
they make the expectation life times more visible.

```Cpp
class immobile
{
public:
MAKE_MOCK1(func, void(int));
};

class movable
{
public:
int i = 0;

static constexpr bool trompeloeil_movable_mock = true;
// allow move construction

MAKE_MOCK1(func, void(int));
};

template <typename T>
T transfer(T t)
{
return t;
}

test(...)
{
auto m = transfer(immobile{}); // compilation error
...
}
test(...)
{
movable m;
auto e = NAMED_REQUIRE_CALL(m, func(3));
auto mm = transfer(std::move(m));
// A call to mm.func() now satisfies e
...
}
test(...)
{
movable m{3};
auto e = NAMED_REQUIRE_CALL(m, func(_))
.LR_WITH(_1 == m.i);
auto mm = transfer(std::move(m)); // Danger! e still refers to m.i.
...
}
```
Loading

0 comments on commit 17edd13

Please sign in to comment.