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
|