-
Notifications
You must be signed in to change notification settings - Fork 0
/
mocking.hpp
108 lines (94 loc) · 3.38 KB
/
mocking.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#pragma once
#include <functional>
// clang can't compile libstdc++'s implementation of this
#if __has_include(<experimental/source_location>) && !defined(__clang__)
#include <experimental/source_location>
#endif
#include <boost/test/test_tools.hpp>
#if __cpp_lib_experimental_source_location
using source_location = std::experimental::source_location;
#else
struct no_source_location {
static no_source_location current() { return {}; }
no_source_location() = default;
const char* file_name() const { return "unknown"; }
int line() const { return 0; }
};
using source_location = no_source_location;
#endif
template <typename decorator_t, typename callable_t>
struct decorated_callable {
decorator_t decorator_;
callable_t callable_;
template <typename D, typename C>
decorated_callable& operator= (const decorated_callable<D, C>& other) {
decorator_ = other.decorator_;
callable_ = other.callable_;
return *this;
}
template <typename D, typename C>
decorated_callable& operator= (decorated_callable<D, C>&& other) {
decorator_ = std::move(other.decorator_);
callable_ = std::move(other.callable_);
return *this;
}
auto& decorator() const { return decorator_; }
template <typename ... args_t>
auto operator() (args_t&& ... args) {
decorator_(std::forward<args_t>(args)...);
return callable_(std::forward<args_t>(args)...);
}
};
class call_count_checker {
struct state {
state(source_location&& location, std::optional<unsigned> expected)
: location_{std::move(location)}
, expected_{expected}
{}
~state() {
if (expected_) {
BOOST_TEST(*expected_ == calls_, "Function defined at " << location_.file_name() << ':' << location_.line()
<< " expected " << *expected_ << " calls, " << calls_ << " seen");
}
}
const source_location location_;
const std::optional<unsigned> expected_;
unsigned calls_ {};
};
std::shared_ptr<state> state_;
public:
call_count_checker(source_location&& location = source_location::current())
: state_{std::make_shared<state>(std::move(location), std::nullopt)}
{}
call_count_checker(unsigned expected, source_location&& location
= source_location::current())
: state_{std::make_shared<state>(std::move(location), expected)}
{}
call_count_checker(const call_count_checker&) = default;
call_count_checker(call_count_checker&&) = default;
call_count_checker& operator= (const call_count_checker&) = default;
call_count_checker& operator= (call_count_checker&&) = default;
unsigned current_count() const { return state_->calls_; }
template <typename ... Args>
void operator() (Args&& ...) const {
state_->calls_ += 1;
}
};
template <typename fn>
decorated_callable<call_count_checker, fn>
expect_calls(unsigned expected, fn&& f,
source_location&& location = source_location::current()) {
return {{expected, std::move(location)}, std::forward<fn>(f)};
}
template <typename>
struct counted_function;
template <typename Ret, typename ... Args>
struct counted_function<Ret(Args...)>
: decorated_callable<call_count_checker, std::function<Ret(Args...)>>
{
using base_t = decorated_callable<call_count_checker, std::function<Ret(Args...)>>;
using base_t::decorated_callable;
using base_t::operator=;
using base_t::decorator;
auto current_count() const { return decorator().current_count(); }
};