Skip to content

Commit

Permalink
Merge pull request #19 from DrCpp/rc-0.5
Browse files Browse the repository at this point in the history
  • Loading branch information
maltekliemann authored Jun 20, 2021
2 parents ceacbfe + 3ace965 commit b10777d
Show file tree
Hide file tree
Showing 51 changed files with 1,996 additions and 412 deletions.
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,35 @@ You should have received a copy of the GNU General Public License
along with DrMock. If not, see <https://www.gnu.org/licenses/>.
-->

# DrMock 0.5.0

Released 2021/06/20

### Added/Changed

* Add `xfail` and `skil` tags to `drtest`

* Add floating point comparison macro `DRTEST_ASSERT_ALMOST_EQUAL` to
`drtest`

* Add complex behaviors to `drmock`

* Add convenience function `addColumns`

* Add convenience functions `template<typename... Deriveds> expect`
and `template<typename... Deriveds>`

* Change polymorphism behavior (`polymorphic` only applies to `expect`
and `transition` calls following `polymorphic`)

* Remove `setIsEqual` from `AbstractBehavior`

### Fixed

* Fix issue #16

* Fix issue #15


# DrMock 0.4.2

Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ cmake_minimum_required (VERSION 3.13)

project(
DrMock
VERSION 0.4.2
VERSION 0.5.0
DESCRIPTION "C++17 testing and mocking framework"
LANGUAGES CXX
)
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ clean:
cd python && make clean

.PHONY: install
install:
install: default
cd build && make install && cd ..
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ features for mock object configuration.

### Announcments

Release v0.4.x is now available. For details, see
Release v0.5.x is now available. For details, see
[changelog](CHANGELOG.md).

### Getting started
Expand Down
179 changes: 179 additions & 0 deletions docs/samples/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,17 @@ adds three columns for integer entries (`"lhs"`, `"rhs"` and
_unique_ name for each column. The name must satisfy the same rules as
all C++ variable names.
As of version `0.5`, you can also use the utility function `addColumns`
to get the same result:
```cpp
DRTEST_DATA(someTestWithTable)
{
drtest::addColumns<int, int, int, std::string>("lhs", "rhs", "expected", "randomStuff");
// ...
}
After adding the columns, the `drtest::addRow` function may then be used
to populate the table:
```cpp
Expand Down Expand Up @@ -376,6 +387,149 @@ Change the test table so that the test check if `2 + 2 == 4` instead of
Total Test time (real) = 0.01 sec
```

## Tags

As of version `0.5`, **DrMock** offers `xfail` and `skip` tags for
tests. A row can be tagged by appending the tag to the `addRow` call, or
by calling `tagRow` seperately:

```cpp
DRTEST_DATA(someTestWithTable)
{
// ...

drtest::addRow(
"This test will be skipped",
2,
2,
4,
std::string{"..."},
drtest::tags::skip
);
drtest::addRow(
"This test is expected to fail",
-2,
-2,
4,
std::string{"0>0"}
);
drtest::tagRow("This test is expected to fail", drtest::tags::xfail);
}
```
Add the above to the file and run `ctest` with `--verbose`. You will see
the following result:
```shell
test 1
Start 1: basicTest
1: Test command: /Users/malte/drmock/samples/basic/build/basicTest
1: Test timeout computed to be: 10000000
1: TEST someTest
1: PASS someTest
1: TEST anotherTest
1: PASS anotherTest
1: TEST exceptionTest
1: PASS exceptionTest
1: TEST someTestWithTable, Small numbers
1: PASS someTestWithTable, Small numbers
1: TEST someTestWithTable, Large numbers
1: PASS someTestWithTable, Large numbers
1: TEST someTestWithTable, This test fails
1: PASS someTestWithTable, This test fails
1: TEST someTestWithTable, This test will be skipped
1: SKIP someTestWithTable, This test will be skipped
1: TEST someTestWithTable, This test is expected to fail
1: XFAIL someTestWithTable, This test is expected to fail (134):
1: (lhs + rhs)
1: -4
1: (expected ==)
1: 4
1:
1: ****************
1: ALL PASS
1/1 Test #1: basicTest ........................ Passed 0.00 sec
100% tests passed, 0 tests failed out of 1
Total Test time (real) = 0.01 sec
```

A test without a corresponding `DRTEST_DATA(...)` can also be skipped or
xfailed by calling `drtest::skip()` or `drtest::xfail()`. Note that the
placement of the call is important. Place `drtest::xfail` somewhere
before the call or assertion that is expected to fail and `drtest::skip`
at the point at which you want to skip to the next test:

```cpp
DRTEST_TEST(...)
{
/* statements */; // These will be executed and may cause failure.
drtest::skip();
// This will not be executed.
}
```
Note that `xfail` currently exists the test after the first failure:
```cpp
DRTEST_TEST(...)
{
drtest::xfail();
DRTEST_ASSERT_EQ(2 + 2, 5);
/* Do something... */; // Will not be executed!
}
```

## Floating point comparison

As of version `0.5`, **DrMock** offers a macro for floating point
comparison, whose "signature" is the following:

```cpp
template<typename T>
DRTEST_ASSERT_ALMOST_EQUAL(T actual, T expected)
```
We expect `T` to be `float`, `double` or `long double`. The macro
performs the following check:
```
|actual - expected| <= abs_tol + rel_tol*|expected|
```
The values `abs_tol` and `rel_tol` are called _absolute_ and _relative
tolerance_, resp. They are, by default, equal to `1e-06`, but may be set
for an entire test file by `#define`-ing the macros `DRTEST_ABS_TOL` and
`DRTEST_REL_TOL` resp., _before including_ the master header `Test.h`:
```cpp
#define DRTEST_ABS_TOL 1e-03
#define DRTEST_REL_TOL 0
#include "Test.h"
```

They may also be set for individual tests by using `drtest::abs_tol` and
`drtest::rel_tol` functions _inside the test_ (thus overriding the default
or the definition using the `#define` directive). For example:

```cpp
DRTEST_TEST(almost_equal_custom)
{
DRTEST_ASSERT_ALMOST_EQUAL(0.000001f, 0.0f); // Uses default/values set by #define.

drtest::abs_tol(1.0);
DRTEST_ASSERT_ALMOST_EQUAL(2.0 + 2.0, 5.0);

// Note that this check is not a symmetric function in
// `(actual, expected)`, unless `rel_tol` is zero.
drtest::rel_tol(0.5);
DRTEST_ASSERT_ALMOST_EQUAL(50.0, 100.0);
DRTEST_ASSERT_TEST_FAIL(DRTEST_ASSERT_ALMOST_EQUAL(100.0, 50.0));
}
```
## Caveats
### Commas in macro arguments
Expand Down Expand Up @@ -482,3 +636,28 @@ macro `DRTEST_NAMESPACE` _before including_ `Test.h`:
#include <DrMock/Test.h>
```
### Row names
Any `snake_case` or `camelCase` name may be used as row name, with the
exception of the emtpy string. Avoid any name containing `DRTEST` or
`DRMOCK`, or `ALL_CAPS` in general.
Furthermore, duplicate row names are not allowed.
### Implicit conversions of number types
Beware of implicit conversions when using the macros! For example, use
`DRTEST_ASSERT_ALMOST_EQUAL(0.9, 1.0)` instead of
`DRTEST_ASSERT_ALMOST_EQUAL(0.9, 1)`. Otherwise, you might end up with an error similar to this one:
```shell
/Users/malte/drmock/tests/Test.cpp:347:3: error: no matching function for call to 'almostEqual'
DRTEST_ASSERT_ALMOST_EQUAL(0.9, 1);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/malte/drmock/src/test/TestMacros.h:109:11: note: expanded from macro 'DRTEST_ASSERT_ALMOST_EQUAL'
if (not drtest::almostEqual(actual, expected)) \
^~~~~~~~~~~~~~~~~~~
/Users/malte/drmock/src/test/Interface.tpp:46:1: note: candidate template ignored: deduced conflicting types for parameter 'T' ('double' vs. 'int')
almostEqual(T actual, T expected)
^
```
109 changes: 104 additions & 5 deletions docs/samples/mock.md
Original file line number Diff line number Diff line change
Expand Up @@ -851,10 +851,10 @@ at least if `bool operator==(const Foo&) const` is not deleted
### Polymorphism
If an interface's method accepts an `std::shared_ptr<B>` or
`std::unique_ptr<B>` with abstract pointee type `B`, then the `Method`
object must be informed informed which derived type to expect using the
`polymorphic` method.
If an interface's method accepts an `std::shared_ptr<Base>` or
`std::unique_ptr<Base>` with abstract pointee type `Base`, then the
`Method` object must be informed informed which derived type to expect
using the `polymorphic` method.
For example,
```cpp
Expand Down Expand Up @@ -892,6 +892,15 @@ foo->mock.func().expects(
);
```

**Beware!** As of version `0.5`, the `polymorphic` call only applies to
`expects` and `transition` calls made _after_ the polymorphic call.

Furthermore, as of version `0.5`, **DrMock** offers convenience
functions `template<typename... Deriveds> expect` and
`template<typename... Deriveds> transition` which call the default
`expect` or `transition`, but make an exception to the currently defined
polymorphic setting.

### Operators

Mocking an operator declared in an interface is not much different from
Expand Down Expand Up @@ -1012,7 +1021,7 @@ DrMockModule(
A regex that describes the pattern that matches the project's
interface header filenames. The regex must contain exactly one
capture group that captures the unadorned filename. The default
value is ``I([a-zA-Z0-9].*)"`.
value is `I([a-zA-Z0-9].*)"`.

##### MOCKFILE
A string that describes the pattern that the project's mock object
Expand Down Expand Up @@ -1122,6 +1131,96 @@ DRMOCK_DUMMY(Foo)
#endif
```
### Complex behaviors
As of version `0.5`, it is possible to define more complex expected
behaviors. For example, you can expect a floating point number to be
almost equal to an expected value. This is done by using an object of
abstract type `template<typename Base> ICompare<Base>`:
```cpp
template<typename Base>
class ICompare
{
public:
virtual ~ICompare() = default;
virtual bool invoke(const Base&) const = 0;
};
```

The `c->invoke(actual)` checks if `actual` is _equivalent_ to the
expected object determined by `c`. For example, here's the
implementation for equality:

```cpp
template<typename Base, typename Derived = Base>
class Equal : public ICompare<Base>
{
public:
Equal(Base expected)
:
expected_{std::move(expected)}
{}

bool
invoke(const Base& actual) const override
{
auto is_equal = detail::IsEqual<Base, Derived>{};
return is_equal(expected_, actual);
}

private:
Base expected_;
};
```
Given a behavior with `Args...`, methods like `expects` can now be
called with any combination of `Args...` or
`std::shared_ptr<ICompare<Args>>...`. **DrMock** also provides
convenience constructors for the default
`std::shared_ptr<ICompare<Base>>` objects. For example, when mocking a
method `void f(std::string, float)`, the user can now do this:
```cpp
b.expects("foo", drmock::almost_equal(1.0f));
```

This expects the arguments to be `"foo"` and a floating point number
almost equal to `1.0f`.

Regarding `almost_equal`, the method of comparison is the same as that
defined in the chapter [basic.md](basic.md). The precision of the
comparison may be set using

```cpp
template<typename T> drmock::almost_equal(T expected, T abs_tol, T rel_tol)
```
or by `#define`-ing `DRTEST_*_TOL`, as described in
[basic.md](basic.md). Beware of using the correct types! The following
call is not allowed:
```cpp
b.expects("foo", drmock::almost_equal(1.0)); // Using double, not float...
```

Failing to following this rule will lead to a potentially confusing
error message like this:

```shell
/Users/malte/drmock/tests/Behavior.cpp:424:5: error: no matching member function for call to 'expects'
b.expects("foo", almost_equal(1.0), poly1);
~~^~~~~~~
/Users/malte/drmock/src/mock/Behavior.h:76:13: note: candidate function not viable: no known conversion from 'std::shared_ptr<ICompare<double>>' to 'detail::expect_t<float>' (aka 'variant<float, std::shared_ptr<ICompare<float>>>') for 2nd argument
Behavior& expects(detail::expect_t<Args>...);
^
/Users/malte/drmock/src/mock/Behavior.h:77:38: note: candidate function template not viable: no known conversion from 'std::shared_ptr<ICompare<double>>' to 'detail::expect_t<float>' (aka 'variant<float, std::shared_ptr<ICompare<float>>>') for 2nd argument
template<typename... Ts> Behavior& expects(detail::expect_t<Args>...);
^
/Users/malte/drmock/src/mock/Behavior.h:70:59: note: candidate function template not viable: requires 0 arguments, but 3 were provided
std::enable_if_t<(std::tuple_size_v<T> > 0), Behavior&> expects();
```

## Fine print: Interface

Here's the definition of the notion of _interface_ in the context of a
Expand Down
Loading

0 comments on commit b10777d

Please sign in to comment.