spies.hpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2014, 2015 Ableton AG, Berlin. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21 //
22 
27 #define BOOST_OPTIONAL_CONFIG_ALLOW_BINDING_TO_RVALUES
28 
29 #include <atria/meta/utils.hpp>
30 
31 #include <ableton/build_system/Warnings.hpp>
32 ABL_DISABLE_WARNINGS
33 #include <boost/variant/static_visitor.hpp>
34 #include <boost/variant/apply_visitor.hpp>
35 #include <boost/optional.hpp>
36 ABL_RESTORE_WARNINGS
37 #include <unordered_set>
38 #include <typeindex>
39 #include <functional>
40 #include <memory>
41 
42 namespace atria {
43 namespace testing {
44 
52 class variant_spy : public boost::static_visitor<>
53 {
54 public:
55  struct all_variants {};
56 
57  template <typename T>
58  void operator()(const T&)
59  {
60  calls_.insert(typeid(T));
61  calls_.insert(typeid(all_variants));
62  }
63 
64  template <typename T = all_variants>
65  std::size_t count() const
66  {
67  return calls_.count(typeid(T));
68  }
69 
70  template<typename VariantT>
71  auto visitor()
73  std::bind(
74  static_cast<void (*) (variant_spy&, const VariantT&)>(
75  &boost::apply_visitor<variant_spy, const VariantT>),
76  std::ref(*this),
77  std::placeholders::_1))
78 
79 private:
80  std::unordered_multiset<std::type_index> calls_;
81 };
82 
83 namespace mocks {
84 
85 template<typename T>
86 struct defaulting
87 {
88  template <typename... Args>
89  T operator() (Args&&...) { return T(); }
90 };
91 
92 template<typename T>
93 struct returning
94 {
95  returning() = default;
96 
97  template<typename Fn>
98  returning(const Fn& mock)
99  : mock_(mock)
100  {}
101 
102  template <typename... Args>
103  T operator() (Args&& ...) {
104  return mock_();
105  }
106 
107 private:
108  std::function<T()> mock_;
109 };
110 
111 } // namespace mocks
112 
113 namespace detail {
114 
115 class spy_base
116 {
117 public:
118  std::size_t count() const
119  {
120  return *count_;
121  }
122 
123  void called()
124  {
125  ++ *count_;
126  }
127 
128 private:
129  std::shared_ptr<std::size_t> count_ = std::make_shared<std::size_t>(0);
130 };
131 
132 } // namespace detail
133 
134 
141 template<typename MockT = mocks::defaulting<void> >
142 class spy_fn : public detail::spy_base
143 {
144  MockT mock_;
145 
146 public:
147  spy_fn() = default;
148 
149  template<typename MockT2>
150  spy_fn(MockT2 mock)
151  : mock_(std::move(mock))
152  {}
153 
154  template<typename MockT2>
155  spy_fn(MockT2 mock, const spy_base& spy)
156  : spy_base(spy)
157  , mock_(std::move(mock))
158  {}
159 
160  template <typename... Args>
161  auto operator() (Args&& ...args)
163  (called(),
164  this->mock_(std::forward<Args>(args)...)))
165 };
166 
170 template <typename Fn>
171 inline auto spy(const Fn& fn)
172  -> spy_fn<Fn>
173 {
174  return spy_fn<Fn>(fn);
175 }
176 
180 inline auto spy()
181  -> spy_fn<>
182 {
183  return spy_fn<>();
184 }
185 
186 namespace detail {
187 
188 template <typename MockT>
189 class scoped_intruder
190 {
191  boost::optional<MockT&> mock_;
192  MockT original_;
193 
194 public:
195  scoped_intruder& operator=(const scoped_intruder&) = delete;
196  scoped_intruder(const scoped_intruder&) = delete;
197 
198  scoped_intruder(scoped_intruder&& other)
199  {
200  swap(*this, other);
201  }
202 
203  scoped_intruder& operator=(scoped_intruder&& other)
204  {
205  if (this != &other)
206  {
207  swap(*this, other);
208  }
209  }
210 
211  scoped_intruder(MockT& mock, MockT replacement)
212  : mock_(mock)
213  , original_(mock)
214  {
215  *mock_ = replacement;
216  }
217 
218  ~scoped_intruder()
219  {
220  if (mock_)
221  {
222  *mock_ = original_;
223  }
224  }
225 
226  template <typename ...Args>
227  auto operator() (Args&& ...args)
228  -> decltype((*mock_)(std::forward<Args>(args)...))
229  {
230  assert(mock_ && "must not call intruder after having moved from it");
231  return (*mock_)(std::forward<Args>(args)...);
232  }
233 
234  friend void swap(scoped_intruder& a, scoped_intruder& b)
235  {
236  using std::swap;
237  swap(a.mock_, b.mock_);
238  swap(a.original_, b.original_);
239  }
240 };
241 
242 } // namespace detail
243 
249 template <typename MockT>
250 inline auto spy_on(MockT& mock)
252 {
253  auto s = spy(mock);
254  return { detail::scoped_intruder<MockT>(mock, s), s };
255 }
256 
262 template <typename MockT, typename FnT>
263 inline auto spy_on(MockT& mock, const FnT& replacement)
265 {
266  auto s = spy(replacement);
267  return { detail::scoped_intruder<MockT>(mock, s), s };
268 }
269 
270 namespace detail {
271 struct default_copy_spy_base_t {};
272 } // namespace detail
273 
277 template <typename BaseT = detail::default_copy_spy_base_t>
278 struct copy_spy : BaseT
279 {
280  using base_t = BaseT;
281 
282  testing::spy_fn<> copied;
283 
284  copy_spy() = default;
285  copy_spy(copy_spy&&) = default;
286  copy_spy& operator=(copy_spy&&) = default;
287 
288  copy_spy(const copy_spy& x)
289  : base_t(x)
290  , copied(x.copied)
291  {
292  copied();
293  }
294 
295  copy_spy& operator=(const copy_spy& x)
296  {
297  base_t::operator=(x);
298  copied = x.copied;
299  copied();
300  return *this;
301  }
302 };
303 
304 } // namespace testing
305 } // namespace atria
auto spy_on(MockT &mock) -> spy_fn< detail::scoped_intruder< MockT > >
Given a functor object mock of a general functor with type erasure (e.g.
Definition: spies.hpp:250
#define ABL_DECLTYPE_RETURN(body_expr)
Utility for defining generic functions with a deduced return type, that are composed of a single expr...
Definition: utils.hpp:109
STL namespace.
Class for spying on functions that take a variant as a parameter.
Definition: spies.hpp:52
Utility for testing how many times an object is copied.
Definition: spies.hpp:278
Functor that counts the number of times it was called.
Definition: spies.hpp:142
C++ amazing templates and reusable implementations awesomeness.
Definition: _doc.hpp:35
auto spy(const Fn &fn) -> spy_fn< Fn >
Returns a spy object that uses fn as mock implementation.
Definition: spies.hpp:171
Fork me on GitHub