mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-10-08 23:29:21 +00:00
Related refactoring to updated of ThrottleList to use new Scheduler.
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
#ifndef LIBTORRENT_HELPERS_MOCK_COMPARE_H
|
||||
#define LIBTORRENT_HELPERS_MOCK_COMPARE_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include <torrent/event.h>
|
||||
#include <torrent/net/socket_address.h>
|
||||
|
||||
// Compare arguments to mock functions with what is expected. The lhs
|
||||
// are the expected arguments, rhs are the ones called with.
|
||||
|
||||
template <typename Arg>
|
||||
inline bool mock_compare_arg(Arg lhs, Arg rhs) { return lhs == rhs; }
|
||||
|
||||
template <int I, typename A, typename... Args>
|
||||
typename std::enable_if<I == 1, int>::type
|
||||
mock_compare_tuple(const std::tuple<A, Args...>& lhs, const std::tuple<Args...>& rhs) {
|
||||
return mock_compare_arg(std::get<I>(lhs), std::get<I - 1>(rhs)) ? 0 : 1;
|
||||
}
|
||||
|
||||
template <int I, typename A, typename... Args>
|
||||
typename std::enable_if<1 < I, int>::type
|
||||
mock_compare_tuple(const std::tuple<A, Args...>& lhs, const std::tuple<Args...>& rhs) {
|
||||
auto res = mock_compare_tuple<I - 1>(lhs, rhs);
|
||||
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
return mock_compare_arg(std::get<I>(lhs), std::get<I - 1>(rhs)) ? 0 : I;
|
||||
}
|
||||
|
||||
//template <typename T, typename std::enable_if<!std::is_const<T>::value, int>::type = 0>
|
||||
template <typename T>
|
||||
struct mock_compare_map {
|
||||
typedef std::map<const T*, const T*> values_type;
|
||||
|
||||
static T* begin_pointer() { return reinterpret_cast<T*>(0x1000); }
|
||||
static T* end_pointer() { return reinterpret_cast<T*>(0x2000); }
|
||||
|
||||
static bool is_key(const T* k) {
|
||||
return k >= begin_pointer() && k < end_pointer();
|
||||
}
|
||||
|
||||
static bool has_key(const T* k) {
|
||||
return values.find(k) != values.end();
|
||||
}
|
||||
|
||||
static bool has_value(const T* v) {
|
||||
return std::find_if(values.begin(), values.end(), [v](typename values_type::value_type& kv) { return v == kv.second; }) != values.end();
|
||||
}
|
||||
|
||||
static const T* get(const T* k) {
|
||||
auto itr = values.find(k);
|
||||
CPPUNIT_ASSERT_MESSAGE("mock_compare_map get failed, not inserted", itr != values.end());
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
static values_type values;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
typename mock_compare_map<T>::values_type mock_compare_map<T>::values;
|
||||
|
||||
template<typename T>
|
||||
void mock_compare_add(T* v) {
|
||||
mock_compare_map<T>::add_value(v);
|
||||
}
|
||||
|
||||
//
|
||||
// Specialize:
|
||||
//
|
||||
|
||||
template <>
|
||||
inline bool mock_compare_arg<sockaddr*>(sockaddr* lhs, sockaddr* rhs) {
|
||||
return lhs != nullptr && rhs != nullptr && torrent::sa_equal(lhs, rhs);
|
||||
}
|
||||
template <>
|
||||
inline bool mock_compare_arg<const sockaddr*>(const sockaddr* lhs, const sockaddr* rhs) {
|
||||
return lhs != nullptr && rhs != nullptr && torrent::sa_equal(lhs, rhs);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool mock_compare_arg<torrent::Event*>(torrent::Event* lhs, torrent::Event* rhs) {
|
||||
if (mock_compare_map<torrent::Event>::is_key(lhs)) {
|
||||
if (!mock_compare_map<torrent::Event>::has_value(rhs)) {
|
||||
mock_compare_map<torrent::Event>::values[lhs] = rhs;
|
||||
return true;
|
||||
}
|
||||
|
||||
return mock_compare_map<torrent::Event>::has_key(lhs) && mock_compare_map<torrent::Event>::get(lhs) == rhs;
|
||||
}
|
||||
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,182 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "test/helpers/mock_function.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <torrent/event.h>
|
||||
#include <torrent/net/socket_address.h>
|
||||
#include <torrent/net/fd.h>
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/random.h>
|
||||
|
||||
#define MOCK_CLEANUP_MAP(MOCK_FUNC) \
|
||||
CPPUNIT_ASSERT_MESSAGE("expected mock function calls not completed for '" #MOCK_FUNC "'", mock_cleanup_map(&MOCK_FUNC) || ignore_assert);
|
||||
#define MOCK_LOG(log_fmt, ...) \
|
||||
lt_log_print(torrent::LOG_MOCK_CALLS, "%s: " log_fmt, __func__, __VA_ARGS__);
|
||||
|
||||
void
|
||||
mock_clear(bool ignore_assert) {
|
||||
MOCK_CLEANUP_MAP(torrent::fd__accept);
|
||||
MOCK_CLEANUP_MAP(torrent::fd__bind);
|
||||
MOCK_CLEANUP_MAP(torrent::fd__close);
|
||||
MOCK_CLEANUP_MAP(torrent::fd__connect);
|
||||
MOCK_CLEANUP_MAP(torrent::fd__fcntl_int);
|
||||
MOCK_CLEANUP_MAP(torrent::fd__listen);
|
||||
MOCK_CLEANUP_MAP(torrent::fd__setsockopt_int);
|
||||
MOCK_CLEANUP_MAP(torrent::fd__socket);
|
||||
|
||||
MOCK_CLEANUP_MAP(torrent::poll_event_open);
|
||||
MOCK_CLEANUP_MAP(torrent::poll_event_close);
|
||||
MOCK_CLEANUP_MAP(torrent::poll_event_closed);
|
||||
MOCK_CLEANUP_MAP(torrent::poll_event_insert_read);
|
||||
MOCK_CLEANUP_MAP(torrent::poll_event_insert_write);
|
||||
MOCK_CLEANUP_MAP(torrent::poll_event_insert_error);
|
||||
MOCK_CLEANUP_MAP(torrent::poll_event_remove_read);
|
||||
MOCK_CLEANUP_MAP(torrent::poll_event_remove_write);
|
||||
MOCK_CLEANUP_MAP(torrent::poll_event_remove_error);
|
||||
|
||||
MOCK_CLEANUP_MAP(torrent::random_uniform_uint16);
|
||||
MOCK_CLEANUP_MAP(torrent::random_uniform_uint32);
|
||||
|
||||
mock_compare_map<torrent::Event>::values.clear();
|
||||
};
|
||||
|
||||
void
|
||||
mock_init() {
|
||||
log_add_group_output(torrent::LOG_MOCK_CALLS, "test_output");
|
||||
mock_clear(true);
|
||||
}
|
||||
|
||||
void
|
||||
mock_cleanup() {
|
||||
mock_clear(false);
|
||||
}
|
||||
|
||||
void
|
||||
mock_redirect_defaults([[maybe_unused]] mock_redirect_flags flags) {
|
||||
mock_redirect(torrent::fd__close, std::function<int(int fildes)>([](int fildes) { return ::close(fildes); }));
|
||||
mock_redirect(torrent::fd__fcntl_int, std::function<int(int fildes, int cmd, int arg)>([](int fildes, int cmd, int arg) { return ::fcntl(fildes, cmd, arg); }));
|
||||
mock_redirect(torrent::fd__setsockopt_int, std::function<int(int socket, int level, int option_name, int option_value)>([](int socket, int level, int option_name, int option_value) { return ::setsockopt(socket, level, option_name, &option_value, sizeof(int)); }));
|
||||
mock_redirect(torrent::fd__socket, std::function<int(int domain, int type, int protocol)>([](int domain, int type, int protocol) { return ::socket(domain, type, protocol); }));
|
||||
}
|
||||
|
||||
namespace torrent {
|
||||
|
||||
//
|
||||
// Mock functions for 'torrent/net/fd.h':
|
||||
//
|
||||
|
||||
int fd__accept(int socket, sockaddr *address, socklen_t *address_len) {
|
||||
MOCK_LOG("entry socket:%i address:%s address_len:%u",
|
||||
socket, torrent::sa_pretty_str(address).c_str(), (unsigned int)(*address_len));
|
||||
auto ret = mock_call<int>(__func__, &torrent::fd__accept, socket, address, address_len);
|
||||
MOCK_LOG("exit socket:%i address:%s address_len:%u",
|
||||
socket, torrent::sa_pretty_str(address).c_str(), (unsigned int)(*address_len));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fd__bind(int socket, const sockaddr *address, socklen_t address_len) {
|
||||
MOCK_LOG("socket:%i address:%s address_len:%u",
|
||||
socket, torrent::sa_pretty_str(address).c_str(), (unsigned int)address_len);
|
||||
return mock_call<int>(__func__, &torrent::fd__bind, socket, address, address_len);
|
||||
}
|
||||
|
||||
int fd__close(int fildes) {
|
||||
MOCK_LOG("filedes:%i", fildes);
|
||||
return mock_call<int>(__func__, &torrent::fd__close, fildes);
|
||||
}
|
||||
|
||||
int fd__connect(int socket, const sockaddr *address, socklen_t address_len) {
|
||||
MOCK_LOG("socket:%i address:%s address_len:%u",
|
||||
socket, torrent::sa_pretty_str(address).c_str(), (unsigned int)address_len);
|
||||
return mock_call<int>(__func__, &torrent::fd__connect, socket, address, address_len);
|
||||
}
|
||||
|
||||
int fd__fcntl_int(int fildes, int cmd, int arg) {
|
||||
MOCK_LOG("filedes:%i cmd:%i arg:%i", fildes, cmd, arg);
|
||||
return mock_call<int>(__func__, &torrent::fd__fcntl_int, fildes, cmd, arg);
|
||||
}
|
||||
|
||||
int fd__listen(int socket, int backlog) {
|
||||
MOCK_LOG("socket:%i backlog:%i", socket, backlog);
|
||||
return mock_call<int>(__func__, &torrent::fd__listen, socket, backlog);
|
||||
}
|
||||
|
||||
int fd__setsockopt_int(int socket, int level, int option_name, int option_value) {
|
||||
MOCK_LOG("socket:%i level:%i option_name:%i option_value:%i",
|
||||
socket, level, option_name, option_value);
|
||||
return mock_call<int>(__func__, &torrent::fd__setsockopt_int, socket, level, option_name, option_value);
|
||||
}
|
||||
|
||||
int fd__socket(int domain, int type, int protocol) {
|
||||
MOCK_LOG("domain:%i type:%i protocol:%i", domain, type, protocol);
|
||||
return mock_call<int>(__func__, &torrent::fd__socket, domain, type, protocol);
|
||||
}
|
||||
|
||||
//
|
||||
// Mock functions for 'torrent/event.h':
|
||||
//
|
||||
|
||||
void poll_event_open(Event* event) {
|
||||
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
|
||||
return mock_call<void>(__func__, &torrent::poll_event_open, event);
|
||||
}
|
||||
|
||||
void poll_event_close(Event* event) {
|
||||
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
|
||||
return mock_call<void>(__func__, &torrent::poll_event_close, event);
|
||||
}
|
||||
|
||||
void poll_event_closed(Event* event) {
|
||||
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
|
||||
return mock_call<void>(__func__, &torrent::poll_event_closed, event);
|
||||
}
|
||||
|
||||
void poll_event_insert_read(Event* event) {
|
||||
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
|
||||
return mock_call<void>(__func__, &torrent::poll_event_insert_read, event);
|
||||
}
|
||||
|
||||
void poll_event_insert_write(Event* event) {
|
||||
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
|
||||
return mock_call<void>(__func__, &torrent::poll_event_insert_write, event);
|
||||
}
|
||||
|
||||
void poll_event_insert_error(Event* event) {
|
||||
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
|
||||
return mock_call<void>(__func__, &torrent::poll_event_insert_error, event);
|
||||
}
|
||||
|
||||
void poll_event_remove_read(Event* event) {
|
||||
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
|
||||
return mock_call<void>(__func__, &torrent::poll_event_remove_read, event);
|
||||
}
|
||||
|
||||
void poll_event_remove_write(Event* event) {
|
||||
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
|
||||
return mock_call<void>(__func__, &torrent::poll_event_remove_write, event);
|
||||
}
|
||||
|
||||
void poll_event_remove_error(Event* event) {
|
||||
MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
|
||||
return mock_call<void>(__func__, &torrent::poll_event_remove_error, event);
|
||||
}
|
||||
|
||||
//
|
||||
// Mock functions for 'torrent/utils/random.h':
|
||||
//
|
||||
|
||||
uint16_t random_uniform_uint16(uint16_t min, uint16_t max) {
|
||||
MOCK_LOG("min:%" PRIu16 " max:%" PRIu16, min, max);
|
||||
return mock_call<uint16_t>(__func__, &torrent::random_uniform_uint16, min, max);
|
||||
}
|
||||
|
||||
uint32_t random_uniform_uint32(uint32_t min, uint32_t max) {
|
||||
MOCK_LOG("min:%" PRIu32 " max:%" PRIu32, min, max);
|
||||
return mock_call<uint32_t>(__func__, &torrent::random_uniform_uint32, min, max);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,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
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "test_fixture.h"
|
||||
|
||||
#include "torrent/utils/log.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void
|
||||
test_fixture::setUp() {
|
||||
mock_init();
|
||||
|
||||
log_add_group_output(torrent::LOG_CONNECTION_BIND, "test_output");
|
||||
log_add_group_output(torrent::LOG_CONNECTION_FD, "test_output");
|
||||
}
|
||||
|
||||
void
|
||||
test_fixture::tearDown() {
|
||||
mock_cleanup();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef LIBTORRENT_HELPER_TEST_FIXTURE_H
|
||||
#define LIBTORRENT_HELPER_TEST_FIXTURE_H
|
||||
|
||||
#include <cppunit/TestFixture.h>
|
||||
|
||||
#include "test/helpers/mock_function.h"
|
||||
|
||||
class test_fixture : public CppUnit::TestFixture {
|
||||
public:
|
||||
void setUp();
|
||||
void tearDown();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "test_main_thread.h"
|
||||
|
||||
#include "globals.h"
|
||||
#include "test/helpers/mock_function.h"
|
||||
#include "torrent/exceptions.h"
|
||||
#include "torrent/poll.h"
|
||||
#include "torrent/net/resolver.h"
|
||||
#include "torrent/utils/scheduler.h"
|
||||
|
||||
std::unique_ptr<TestMainThread>
|
||||
TestMainThread::create() {
|
||||
if (torrent::Poll::slot_create_poll() == nullptr)
|
||||
set_create_poll();
|
||||
|
||||
// Needs to be called before Thread is created.
|
||||
mock_redirect_defaults();
|
||||
|
||||
auto thread = new TestMainThread();
|
||||
return std::unique_ptr<TestMainThread>(thread);
|
||||
}
|
||||
|
||||
TestMainThread::TestMainThread() {}
|
||||
|
||||
TestMainThread::~TestMainThread() {
|
||||
m_self = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
TestMainThread::init_thread() {
|
||||
if (!torrent::Poll::slot_create_poll())
|
||||
throw torrent::internal_error("ThreadMain::init_thread(): Poll::slot_create_poll() not valid.");
|
||||
|
||||
m_poll = std::unique_ptr<torrent::Poll>(torrent::Poll::slot_create_poll()());
|
||||
m_resolver = std::make_unique<torrent::net::Resolver>();
|
||||
m_state = STATE_INITIALIZED;
|
||||
|
||||
//m_instrumentation_index = INSTRUMENTATION_POLLING_DO_POLL_MAIN - INSTRUMENTATION_POLLING_DO_POLL;
|
||||
|
||||
init_thread_local();
|
||||
}
|
||||
|
||||
void
|
||||
TestMainThread::call_events() {
|
||||
process_callbacks();
|
||||
}
|
||||
|
||||
std::chrono::microseconds
|
||||
TestMainThread::next_timeout() {
|
||||
return std::chrono::microseconds(10min);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef TEST_HELPERS_TEST_MAIN_THREAD_H
|
||||
#define TEST_HELPERS_TEST_MAIN_THREAD_H
|
||||
|
||||
#include <memory>
|
||||
#include <torrent/common.h>
|
||||
#include <torrent/utils/thread.h>
|
||||
|
||||
#include "test/helpers/test_thread.h"
|
||||
|
||||
class TestMainThread : public torrent::utils::Thread {
|
||||
public:
|
||||
static std::unique_ptr<TestMainThread> create();
|
||||
|
||||
~TestMainThread() override;
|
||||
|
||||
const char* name() const override { return "rtorrent test main"; }
|
||||
|
||||
void init_thread() override;
|
||||
|
||||
void test_set_cached_time(std::chrono::microseconds t) { set_cached_time(365 * 24h + t); }
|
||||
void test_process_events_without_cached_time() { process_events_without_cached_time(); }
|
||||
|
||||
private:
|
||||
TestMainThread();
|
||||
|
||||
void call_events() override;
|
||||
std::chrono::microseconds next_timeout() override;
|
||||
};
|
||||
|
||||
#endif // TEST_HELPERS_TEST_MAIN_THREAD_H
|
||||
@@ -0,0 +1,102 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "test_thread.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/poll.h>
|
||||
|
||||
#include "test/helpers/mock_function.h"
|
||||
|
||||
const int test_thread::test_flag_pre_stop;
|
||||
const int test_thread::test_flag_long_timeout;
|
||||
|
||||
const int test_thread::test_flag_do_work;
|
||||
const int test_thread::test_flag_pre_poke;
|
||||
const int test_thread::test_flag_post_poke;
|
||||
|
||||
// TODO: Remove PollSelect.
|
||||
|
||||
torrent::Poll*
|
||||
create_poll() {
|
||||
torrent::Poll* poll = torrent::Poll::create(256);
|
||||
|
||||
if (poll == nullptr)
|
||||
throw torrent::internal_error("Unable to create poll object");
|
||||
|
||||
return poll;
|
||||
}
|
||||
|
||||
void
|
||||
set_create_poll() {
|
||||
torrent::Poll::slot_create_poll() = []() {
|
||||
return create_poll();
|
||||
};
|
||||
}
|
||||
|
||||
std::unique_ptr<test_thread>
|
||||
test_thread::create() {
|
||||
// Needs to be called before Thread is created.
|
||||
mock_redirect_defaults();
|
||||
|
||||
auto thread = new test_thread();
|
||||
return std::unique_ptr<test_thread>(thread);
|
||||
}
|
||||
|
||||
test_thread::test_thread() :
|
||||
m_test_state(TEST_NONE),
|
||||
m_test_flags(0) {
|
||||
}
|
||||
|
||||
test_thread::~test_thread() {
|
||||
if (is_active())
|
||||
stop_thread_wait();
|
||||
|
||||
m_self = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
test_thread::init_thread() {
|
||||
m_state = STATE_INITIALIZED;
|
||||
m_test_state = TEST_PRE_START;
|
||||
|
||||
m_poll = std::unique_ptr<torrent::Poll>(create_poll());
|
||||
}
|
||||
|
||||
void
|
||||
test_thread::call_events() {
|
||||
m_loop_count++;
|
||||
|
||||
if ((m_test_flags & test_flag_pre_stop) && m_test_state == TEST_PRE_START && m_state == STATE_ACTIVE)
|
||||
m_test_state = TEST_PRE_STOP;
|
||||
|
||||
if ((m_flags & flag_do_shutdown)) {
|
||||
if ((m_flags & flag_did_shutdown))
|
||||
throw torrent::internal_error("Already trigged shutdown.");
|
||||
|
||||
m_flags |= flag_did_shutdown;
|
||||
throw torrent::shutdown_exception();
|
||||
}
|
||||
|
||||
if ((m_test_flags & test_flag_pre_poke)) {
|
||||
}
|
||||
|
||||
if ((m_test_flags & test_flag_do_work)) {
|
||||
usleep(10 * 1000); // TODO: Don't just sleep, as that give up core.
|
||||
m_test_flags &= ~test_flag_do_work;
|
||||
}
|
||||
|
||||
if ((m_test_flags & test_flag_post_poke)) {
|
||||
}
|
||||
|
||||
process_callbacks();
|
||||
}
|
||||
|
||||
std::chrono::microseconds
|
||||
test_thread::next_timeout() {
|
||||
if ((m_test_flags & test_flag_long_timeout))
|
||||
return std::chrono::microseconds(10s);
|
||||
else
|
||||
return std::chrono::microseconds(100ms);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#ifndef TEST_HELPERS_TEST_THREAD_H
|
||||
#define TEST_HELPERS_TEST_THREAD_H
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#include "torrent/common.h"
|
||||
#include "torrent/utils/thread.h"
|
||||
|
||||
class test_thread : public torrent::utils::Thread {
|
||||
public:
|
||||
enum test_state {
|
||||
TEST_NONE,
|
||||
TEST_PRE_START,
|
||||
TEST_PRE_STOP,
|
||||
TEST_STOP
|
||||
};
|
||||
|
||||
static const int test_flag_pre_stop = 0x1;
|
||||
static const int test_flag_long_timeout = 0x2;
|
||||
|
||||
static const int test_flag_do_work = 0x100;
|
||||
static const int test_flag_pre_poke = 0x200;
|
||||
static const int test_flag_post_poke = 0x400;
|
||||
|
||||
static std::unique_ptr<test_thread> create();
|
||||
|
||||
~test_thread() override;
|
||||
|
||||
int test_state() const { return m_test_state; }
|
||||
|
||||
bool is_state(int state) const { return m_state == state; }
|
||||
bool is_test_state(int state) const { return m_test_state == state; }
|
||||
bool is_test_flags(int flags) const { return (m_test_flags & flags) == flags; }
|
||||
bool is_not_test_flags(int flags) const { return !(m_test_flags & flags); }
|
||||
|
||||
// Loop count increments twice each loop.
|
||||
int loop_count() const { return m_loop_count; }
|
||||
|
||||
const char* name() const override { return "test_thread"; }
|
||||
|
||||
void init_thread() override;
|
||||
|
||||
void set_pre_stop() { m_test_flags |= test_flag_pre_stop; }
|
||||
void set_test_flag(int flags) { m_test_flags |= flags; }
|
||||
|
||||
private:
|
||||
test_thread();
|
||||
|
||||
void call_events() override;
|
||||
std::chrono::microseconds next_timeout() override;
|
||||
|
||||
std::atomic_int m_test_state;
|
||||
std::atomic_int m_test_flags;
|
||||
std::atomic_int m_loop_count{0};
|
||||
};
|
||||
|
||||
void set_create_poll();
|
||||
|
||||
// TODO: Need better cleanup here.
|
||||
// TODO: Replace these with class that holds the threads and cleans them up on destruction.
|
||||
|
||||
#define SETUP_THREAD_DISK() \
|
||||
auto thread_test = test_thread::create(); \
|
||||
thread_test->init_thread(); \
|
||||
torrent::ThreadDisk::create_thread(); \
|
||||
torrent::thread_disk()->init_thread(); \
|
||||
torrent::thread_disk()->start_thread();
|
||||
|
||||
#define CLEANUP_THREAD_DISK() \
|
||||
torrent::ThreadDisk::destroy_thread();
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user