File: mock_function.h

package info (click to toggle)
libtorrent 0.15.7-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 5,152 kB
  • sloc: cpp: 35,007; sh: 4,488; ansic: 4,397; makefile: 575; xml: 163
file content (176 lines) | stat: -rw-r--r-- 5,831 bytes parent folder | download | duplicates (2)
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#ifndef LIBTORRENT_HELPERS_MOCK_FUNCTION_H
#define LIBTORRENT_HELPERS_MOCK_FUNCTION_H

#include <functional>
#include <map>
#include <mutex>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <cppunit/extensions/HelperMacros.h>

#include "test/helpers/mock_compare.h"

namespace torrent {
extern int fd__accept(int socket, sockaddr *address, socklen_t *address_len);
extern int fd__bind(int socket, const sockaddr *address, socklen_t address_len);
extern int fd__close(int fildes);
extern int fd__connect(int socket, const sockaddr *address, socklen_t address_len);
extern int fd__fcntl_int(int fildes, int cmd, int arg);
extern int fd__listen(int socket, int backlog);
extern int fd__setsockopt_int(int socket, int level, int option_name, int option_value);
extern int fd__socket(int domain, int type, int protocol);
}

enum mock_redirect_flags {
  mock_redirect_all = ~0,
};

void mock_init();
void mock_cleanup();
void mock_redirect_defaults(mock_redirect_flags flags = mock_redirect_all);

template<typename R, typename... Args>
struct mock_function_map {
  typedef std::tuple<R, Args...> call_type;
  typedef std::vector<call_type> call_list_type;
  typedef std::map<void*, call_list_type> func_map_type;

  typedef std::function<R (Args...)> function_type;
  typedef std::map<void*, function_type> redirect_map_type;

  static std::mutex        mutex;
  static func_map_type     functions;
  static redirect_map_type redirects;

  static bool cleanup(void* fn) {
    std::lock_guard<std::mutex> lock(mutex);

    redirects.erase(fn);
    return functions.erase(fn) == 0;
  }

  static R ret_erase(void* fn) {
    auto itr = functions.find(fn);
    auto ret = std::get<0>(itr->second.front());
    itr->second.erase(itr->second.begin());

    if (itr->second.empty())
      functions.erase(itr);

    return ret;
  }
};

template<typename R, typename... Args>
std::mutex mock_function_map<R, Args...>::mutex;
template<typename R, typename... Args>
typename mock_function_map<R, Args...>::func_map_type mock_function_map<R, Args...>::functions;
template<typename R, typename... Args>
typename mock_function_map<R, Args...>::redirect_map_type mock_function_map<R, Args...>::redirects;

struct mock_void {};

template<typename R, typename... Args>
struct mock_function_type {
  typedef mock_function_map<R, Args...> type;

  static int compare_expected(typename type::call_type lhs, Args... rhs) {
    return mock_compare_tuple<sizeof...(Args)>(lhs, std::make_tuple(rhs...));
  }

  static R    ret_erase(void* fn) { return type::ret_erase(fn); }

  static bool has_redirect(void* fn) {
    std::lock_guard<std::mutex> lock(type::mutex);
    return type::redirects.find(fn) != type::redirects.end();
  }

  static R    call_redirect(void* fn, Args... args) {
    std::lock_guard<std::mutex> lock(type::mutex);
    return type::redirects.find(fn)->second(args...);
  }
};

template<typename... Args>
struct mock_function_type<void, Args...> {
  typedef mock_function_map<mock_void, Args...> type;

  static int compare_expected(typename type::call_type lhs, Args... rhs) {
    return mock_compare_tuple<sizeof...(Args)>(lhs, std::make_tuple(rhs...));
  }

  static void ret_erase(void* fn) { type::ret_erase(fn); }

  static bool has_redirect(void* fn) {
    std::lock_guard<std::mutex> lock(type::mutex);
    return type::redirects.find(fn) != type::redirects.end();
  }

  static void call_redirect(void* fn, Args... args) {
    std::lock_guard<std::mutex> lock(type::mutex);
    type::redirects.find(fn)->second(args...);
  }
};

template<typename R, typename... Args>
bool
mock_cleanup_map(R fn[[gnu::unused]](Args...)) {
  return mock_function_type<R, Args...>::type::cleanup(reinterpret_cast<void*>(fn));
}

template<typename R, typename... Args>
void
mock_expect(R fn(Args...), R ret, Args... args) {
  typedef mock_function_map<R, Args...> mock_map;
  std::lock_guard<std::mutex> lock(mock_map::mutex);
  mock_map::functions[reinterpret_cast<void*>(fn)].push_back(std::tuple<R, Args...>(ret, args...));
}

template<typename... Args>
void
mock_expect(void fn(Args...), Args... args) {
  typedef mock_function_map<mock_void, Args...> mock_map;
  std::lock_guard<std::mutex> lock(mock_map::mutex);
  mock_map::functions[reinterpret_cast<void*>(fn)].push_back(std::tuple<mock_void, Args...>(mock_void(), args...));
}

template<typename R, typename... Args>
void
mock_redirect(R fn(Args...), std::function<R (Args...)> func) {
  typedef mock_function_map<R, Args...> mock_map;
  std::lock_guard<std::mutex> lock(mock_map::mutex);
  mock_map::redirects[reinterpret_cast<void*>(fn)] = func;
}

template<typename R, typename... Args>
auto
mock_call_direct(std::string name, R fn(Args...), Args... args) -> decltype(fn(args...)) {
  typedef mock_function_type<R, Args...> mock_type;

  std::lock_guard<std::mutex> lock(mock_type::type::mutex);

  auto itr = mock_type::type::functions.find(reinterpret_cast<void*>(fn));
  CPPUNIT_ASSERT_MESSAGE(("mock_call expected function calls exhausted by '" + name + "'").c_str(),
                         itr != mock_type::type::functions.end());

  auto mismatch_arg = mock_type::compare_expected(itr->second.front(), args...);
  CPPUNIT_ASSERT_MESSAGE(("mock_call expected function call argument " + std::to_string(mismatch_arg) + " mismatch for '" + name + "'").c_str(),
                         mismatch_arg == 0);

  return mock_type::ret_erase(reinterpret_cast<void*>(fn));
}

template<typename R, typename... Args>
auto
mock_call(std::string name, R fn(Args...), Args... args) -> decltype(fn(args...)) {
  typedef mock_function_type<R, Args...> mock_type;

  if (mock_type::has_redirect(reinterpret_cast<void*>(fn)))
    return mock_type::call_redirect(reinterpret_cast<void*>(fn), args...);

  return mock_call_direct(name, fn, args...);
}

#endif