refactor: use GTest for running tests (#1383)
* refactor: use google-test on libtransmission tests
This commit is contained in:
parent
6da4a4dfad
commit
677dc73eac
|
@ -22,3 +22,6 @@
|
|||
path = third-party/miniupnpc
|
||||
url = https://github.com/transmission/miniupnpc
|
||||
branch = post-2.0.20170509-transmission
|
||||
[submodule "third-party/googletest"]
|
||||
path = third-party/googletest
|
||||
url = https://github.com/google/googletest.git
|
||||
|
|
101
CMakeLists.txt
101
CMakeLists.txt
|
@ -111,6 +111,7 @@ set(QT5_MINIMUM 5.2)
|
|||
|
||||
if(WIN32)
|
||||
foreach(L C CXX)
|
||||
set(CMAKE_${L}_FLAGS "${CMAKE_${L}_FLAGS} -DWIN32")
|
||||
# Target version (Vista and up)
|
||||
set(CMAKE_${L}_FLAGS "${CMAKE_${L}_FLAGS} -DWINVER=0x0600 -D_WIN32_WINNT=0x0600")
|
||||
# Use Unicode API (although we always use W or A names explicitly)
|
||||
|
@ -375,10 +376,14 @@ if(WIN32)
|
|||
endforeach()
|
||||
endif()
|
||||
|
||||
## Compiler standard version
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS "3.1")
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17")
|
||||
elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17")
|
||||
endif()
|
||||
else()
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
|
@ -387,48 +392,92 @@ else()
|
|||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||
set(NEEDED_COMPILER_FLAGS
|
||||
-Wall
|
||||
### Compiler Warnings
|
||||
|
||||
set(C_WARNING_FLAGS)
|
||||
set(CXX_WARNING_FLAGS)
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
set(WARNING_CANDIDATES
|
||||
-W
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wcast-align
|
||||
-Wduplicated-cond
|
||||
-Wextra-semi
|
||||
-Wextra-semi-stmt
|
||||
-Wextra-tokens
|
||||
-Wfloat-equal
|
||||
-Wgnu
|
||||
-Winit-self
|
||||
-Wint-in-bool-context
|
||||
-Wlogical-op
|
||||
-Wmissing-format-attribute
|
||||
-Wnested-externs
|
||||
-Wnull-dereference
|
||||
-Wpointer-arith
|
||||
-Wredundant-decls
|
||||
-Wundef
|
||||
-Wredundant-move
|
||||
-Wreorder-ctor
|
||||
-Wrestrict
|
||||
-Wreturn-std-move
|
||||
-Wself-assign
|
||||
-Wself-move
|
||||
-Wsemicolon-before-method-body
|
||||
-Wsentinel
|
||||
-Wshadow
|
||||
-Wsign-compare
|
||||
-Wsometimes-uninitialized
|
||||
-Wstrict-prototypes
|
||||
-Wstring-conversion
|
||||
-Wsuggest-destructor-override
|
||||
-Wsuggest-override
|
||||
-Wuninitialized
|
||||
-Wunreachable-code
|
||||
-Wunused
|
||||
-Wunused-const-variable
|
||||
-Wunused-parameter
|
||||
-Wwrite-strings)
|
||||
|
||||
if(NOT CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_VERSION VERSION_GREATER "3.3")
|
||||
list(APPEND NEEDED_COMPILER_FLAGS
|
||||
-Wextra
|
||||
-Winit-self)
|
||||
endif()
|
||||
-Wunused-result
|
||||
-Wwrite-strings
|
||||
)
|
||||
|
||||
if(MINGW)
|
||||
# Disable excessive warnings since we're using __USE_MINGW_ANSI_STDIO
|
||||
# Hopefully, any potential issues will be spotted on other platforms
|
||||
list(APPEND NEEDED_COMPILER_FLAGS -Wno-format)
|
||||
list(APPEND WARNING_CANDIDATES -Wno-format)
|
||||
else()
|
||||
list(APPEND NEEDED_COMPILER_FLAGS -Wformat-security)
|
||||
list(APPEND WARNING_CANDIDATES -Wformat-security)
|
||||
endif()
|
||||
|
||||
set(NEEDED_C_COMPILER_FLAGS
|
||||
${NEEDED_COMPILER_FLAGS}
|
||||
-Winline
|
||||
-Wmissing-declarations
|
||||
-Wnested-externs
|
||||
-Wstrict-prototypes)
|
||||
string(REPLACE ";" " " NEEDED_C_COMPILER_FLAGS_STRING "${NEEDED_C_COMPILER_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NEEDED_C_COMPILER_FLAGS_STRING}")
|
||||
foreach(FLAG ${WARNING_CANDIDATES})
|
||||
tr_make_id("${FLAG}" FLAG_ID)
|
||||
|
||||
set(NEEDED_CXX_COMPILER_FLAGS
|
||||
${NEEDED_COMPILER_FLAGS})
|
||||
string(REPLACE ";" " " NEEDED_CXX_COMPILER_FLAGS_STRING "${NEEDED_CXX_COMPILER_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${NEEDED_CXX_COMPILER_FLAGS_STRING}")
|
||||
# if available, add to C warnings
|
||||
set(CACHE_ID "${CMAKE_C_COMPILER_ID}_C_HAS${FLAG_ID}")
|
||||
string(TOLOWER "${CACHE_ID}" CACHE_ID)
|
||||
check_c_compiler_flag(${FLAG} ${CACHE_ID})
|
||||
if (${CACHE_ID})
|
||||
list(APPEND C_WARNING_FLAGS ${FLAG})
|
||||
endif()
|
||||
|
||||
# if available, add to CXX warnings
|
||||
set(CACHE_ID "${CMAKE_CXX_COMPILER_ID}_CXX_HAS${FLAG_ID}")
|
||||
string(TOLOWER "${CACHE_ID}" CACHE_ID)
|
||||
check_cxx_compiler_flag(${FLAG} ${CACHE_ID})
|
||||
if (${CACHE_ID})
|
||||
list(APPEND CXX_WARNING_FLAGS ${FLAG})
|
||||
endif()
|
||||
|
||||
unset(CACHE_ID)
|
||||
unset(FLAG_ID)
|
||||
endforeach()
|
||||
|
||||
unset(WARNING_CANDIDATES)
|
||||
|
||||
###
|
||||
|
||||
include(LargeFileSupport)
|
||||
|
||||
set(NEEDED_HEADERS
|
||||
|
@ -535,7 +584,9 @@ if(NOT CMAKE_VERSION VERSION_LESS "3.7.2")
|
|||
endif()
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
function(tr_install_web DST_DIR)
|
||||
|
|
|
@ -24,7 +24,8 @@ SUBDIRS = \
|
|||
$(CLI_DIR) \
|
||||
$(GTK_DIR) \
|
||||
$(MAC_DIR) \
|
||||
web
|
||||
web \
|
||||
tests
|
||||
|
||||
EXTRA_DIST = \
|
||||
qt \
|
||||
|
|
|
@ -27,6 +27,7 @@ xargs \
|
|||
|
||||
find \
|
||||
qt \
|
||||
tests \
|
||||
\( -name '*.cc' -o -name '*.h' \) \
|
||||
-print0 |
|
||||
xargs \
|
||||
|
|
|
@ -16,7 +16,7 @@ AC_CONFIG_MACRO_DIR([m4])
|
|||
|
||||
dnl AM_CONFIG_HEADER(config.h)
|
||||
AC_CONFIG_SRCDIR(libtransmission/transmission.h)
|
||||
AM_INIT_AUTOMAKE([1.9 tar-pax no-dist-gzip dist-xz foreign])
|
||||
AM_INIT_AUTOMAKE([1.9 tar-pax no-dist-gzip dist-xz foreign subdir-objects])
|
||||
LT_INIT
|
||||
LT_LIB_M
|
||||
|
||||
|
@ -83,7 +83,7 @@ AC_PROG_CXX
|
|||
AC_C_INLINE
|
||||
if test "x$GCC" = "xyes" ; then
|
||||
|
||||
CFLAGS="$CFLAGS -std=gnu99 -ggdb3 -Wall -W -Wpointer-arith -Wformat-security -Wundef -Wcast-align -Wstrict-prototypes -Wmissing-declarations -Wmissing-format-attribute -Wredundant-decls -Wnested-externs -Wunused-parameter -Wwrite-strings -Winline -Wfloat-equal"
|
||||
CFLAGS="$CFLAGS -std=gnu99 -ggdb3 -Wall -W -Wpointer-arith -Wformat-security -Wundef -Wcast-align -Wstrict-prototypes -Wmissing-declarations -Wmissing-format-attribute -Wredundant-decls -Wnested-externs -Wunused-parameter -Wwrite-strings -Wfloat-equal"
|
||||
|
||||
dnl figure out gcc version
|
||||
AC_MSG_CHECKING([gcc version])
|
||||
|
@ -675,6 +675,9 @@ AC_CONFIG_FILES([Makefile
|
|||
web/style/transmission/images/buttons/Makefile
|
||||
web/javascript/Makefile
|
||||
web/javascript/jquery/Makefile
|
||||
tests/Makefile
|
||||
tests/gtest/Makefile
|
||||
tests/libtransmission/Makefile
|
||||
po/Makefile.in])
|
||||
|
||||
AC_OUTPUT
|
||||
|
|
|
@ -6,6 +6,9 @@ endif()
|
|||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
include_directories(
|
||||
SYSTEM
|
||||
${CURL_INCLUDE_DIRS}
|
||||
${EVENT2_INCLUDE_DIRS}
|
||||
)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
project(trgtk)
|
||||
|
||||
add_compile_options(${C_WARNING_FLAGS})
|
||||
|
||||
if(WITH_LIBAPPINDICATOR)
|
||||
add_definitions(-DHAVE_LIBAPPINDICATOR)
|
||||
endif()
|
||||
|
@ -119,6 +121,9 @@ set(${PROJECT_NAME}_HEADERS
|
|||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${PROJECT_BINARY_DIR}
|
||||
)
|
||||
include_directories(
|
||||
SYSTEM
|
||||
${LIBAPPINDICATOR_INCLUDE_DIRS}
|
||||
${GTK_INCLUDE_DIRS}
|
||||
${CURL_INCLUDE_DIRS}
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
#include "util.h"
|
||||
|
||||
#define MY_CONFIG_NAME "transmission"
|
||||
#define MY_READABLE_NAME "transmission-gtk"
|
||||
|
||||
static char* gl_confdir = NULL;
|
||||
|
||||
|
|
|
@ -656,7 +656,7 @@ static char* get_short_date_string(time_t t)
|
|||
tr_localtime_r(&t, &tm);
|
||||
strftime(buf, sizeof(buf), "%d %b %Y", &tm);
|
||||
return g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
|
||||
};
|
||||
}
|
||||
|
||||
static void refreshInfo(struct DetailsImpl* di, tr_torrent** torrents, int n)
|
||||
{
|
||||
|
@ -1993,7 +1993,7 @@ static void setPeerViewColumns(GtkTreeView* peer_view)
|
|||
so create a non-visible column and assign it as the
|
||||
'expander column. */
|
||||
{
|
||||
GtkTreeViewColumn* c = gtk_tree_view_column_new();
|
||||
c = gtk_tree_view_column_new();
|
||||
gtk_tree_view_column_set_visible(c, FALSE);
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(peer_view), c);
|
||||
gtk_tree_view_set_expander_column(GTK_TREE_VIEW(peer_view), c);
|
||||
|
@ -2374,7 +2374,6 @@ static void refreshTracker(struct DetailsImpl* di, tr_torrent** torrents, int n)
|
|||
if (g_hash_table_lookup(hash, gstr->str) == NULL)
|
||||
{
|
||||
GtkTreePath* p;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeRowReference* ref;
|
||||
|
||||
gtk_list_store_insert_with_values(store, &iter, -1,
|
||||
|
|
|
@ -22,9 +22,7 @@
|
|||
#include "tr-prefs.h"
|
||||
#include "util.h"
|
||||
|
||||
#define TR_DOWNLOAD_KEY "tr-download-key"
|
||||
#define TR_COLUMN_ID_KEY "tr-model-column-id-key"
|
||||
#define TR_PRIORITY_KEY "tr-priority-key"
|
||||
|
||||
enum
|
||||
{
|
||||
|
|
24
gtk/icons.c
24
gtk/icons.c
|
@ -60,19 +60,17 @@ static int get_size_in_pixels(GtkWidget* widget, GtkIconSize icon_size)
|
|||
|
||||
static IconCache* icon_cache_new(GtkWidget* for_widget, int icon_size)
|
||||
{
|
||||
IconCache* icon_cache;
|
||||
|
||||
g_return_val_if_fail(for_widget != NULL, NULL);
|
||||
|
||||
icon_cache = g_new0(IconCache, 1);
|
||||
icon_cache->icon_theme = gtk_icon_theme_get_for_screen(gtk_widget_get_screen(for_widget));
|
||||
icon_cache->icon_size = get_size_in_pixels(for_widget, icon_size);
|
||||
icon_cache->cache = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref);
|
||||
IconCache* icons = g_new0(IconCache, 1);
|
||||
icons->icon_theme = gtk_icon_theme_get_for_screen(gtk_widget_get_screen(for_widget));
|
||||
icons->icon_size = get_size_in_pixels(for_widget, icon_size);
|
||||
icons->cache = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref);
|
||||
|
||||
g_hash_table_insert(icon_cache->cache, (void*)VOID_PIXBUF_KEY, create_void_pixbuf(icon_cache->icon_size,
|
||||
icon_cache->icon_size));
|
||||
g_hash_table_insert(icons->cache, (void*)VOID_PIXBUF_KEY, create_void_pixbuf(icons->icon_size,
|
||||
icons->icon_size));
|
||||
|
||||
return icon_cache;
|
||||
return icons;
|
||||
}
|
||||
|
||||
static char const* _icon_cache_get_icon_key(GIcon* icon)
|
||||
|
@ -183,7 +181,7 @@ static GdkPixbuf* _get_icon_pixbuf(GIcon* icon, int size, GtkIconTheme* theme)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static GdkPixbuf* icon_cache_get_mime_type_icon(IconCache* icon_cache, char const* mime_type)
|
||||
static GdkPixbuf* icon_cache_get_mime_type_icon(IconCache* icons, char const* mime_type)
|
||||
{
|
||||
GIcon* icon;
|
||||
char const* key = NULL;
|
||||
|
@ -197,7 +195,7 @@ static GdkPixbuf* icon_cache_get_mime_type_icon(IconCache* icon_cache, char cons
|
|||
key = VOID_PIXBUF_KEY;
|
||||
}
|
||||
|
||||
pixbuf = g_hash_table_lookup(icon_cache->cache, key);
|
||||
pixbuf = g_hash_table_lookup(icons->cache, key);
|
||||
|
||||
if (pixbuf != NULL)
|
||||
{
|
||||
|
@ -206,11 +204,11 @@ static GdkPixbuf* icon_cache_get_mime_type_icon(IconCache* icon_cache, char cons
|
|||
return pixbuf;
|
||||
}
|
||||
|
||||
pixbuf = _get_icon_pixbuf(icon, icon_cache->icon_size, icon_cache->icon_theme);
|
||||
pixbuf = _get_icon_pixbuf(icon, icons->icon_size, icons->icon_theme);
|
||||
|
||||
if (pixbuf != NULL)
|
||||
{
|
||||
g_hash_table_insert(icon_cache->cache, (gpointer)key, g_object_ref(pixbuf));
|
||||
g_hash_table_insert(icons->cache, (gpointer)key, g_object_ref(pixbuf));
|
||||
}
|
||||
|
||||
g_object_unref(G_OBJECT(icon));
|
||||
|
|
|
@ -74,7 +74,7 @@ static void get_capabilities_callback(GObject* source, GAsyncResult* res, gpoint
|
|||
g_variant_unref(result);
|
||||
}
|
||||
|
||||
static void g_signal_callback(GDBusProxy* proxy UNUSED, char* sender_name UNUSED, char* signal_name, GVariant* params,
|
||||
static void g_signal_callback(GDBusProxy* dbus_proxy UNUSED, char* sender_name UNUSED, char* signal_name, GVariant* params,
|
||||
gpointer user_data UNUSED)
|
||||
{
|
||||
guint id;
|
||||
|
|
|
@ -87,7 +87,7 @@ static int core_is_disposed(TrCore const* core)
|
|||
return core == NULL || core->priv->sorted_model == NULL;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE(TrCore, tr_core, G_TYPE_OBJECT, G_ADD_PRIVATE(TrCore));
|
||||
G_DEFINE_TYPE_WITH_CODE(TrCore, tr_core, G_TYPE_OBJECT, G_ADD_PRIVATE(TrCore))
|
||||
|
||||
static void core_dispose(GObject* o)
|
||||
{
|
||||
|
|
|
@ -98,7 +98,7 @@ static gboolean spun_cb_idle(gpointer spin)
|
|||
struct spin_idle_data* data = g_object_get_data(o, IDLE_DATA);
|
||||
|
||||
/* has the user stopped making changes? */
|
||||
if (g_timer_elapsed(data->last_change, NULL) > 0.33F)
|
||||
if (g_timer_elapsed(data->last_change, NULL) > 0.33)
|
||||
{
|
||||
/* update the core */
|
||||
tr_quark const key = GPOINTER_TO_INT(g_object_get_data(o, PREF_KEY));
|
||||
|
@ -804,8 +804,6 @@ static GtkWidget* remotePage(GObject* core)
|
|||
GtkCellRenderer* r;
|
||||
GtkTreeSelection* sel;
|
||||
GtkTreeView* v;
|
||||
GtkWidget* w;
|
||||
GtkWidget* h;
|
||||
|
||||
page->store = GTK_LIST_STORE(m);
|
||||
w = gtk_tree_view_new_with_model(m);
|
||||
|
|
|
@ -710,13 +710,13 @@ GtkWidget* gtr_window_new(GtkApplication* app, GtkUIManager* ui_mgr, TrCore* cor
|
|||
|
||||
{
|
||||
/* this is to determine the maximum width/height for the label */
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
PangoLayout* pango_layout;
|
||||
pango_layout = gtk_widget_create_pango_layout(ul_lb, "999.99 kB/s");
|
||||
pango_layout_get_pixel_size(pango_layout, &w, &h);
|
||||
gtk_widget_set_size_request(ul_lb, w, h);
|
||||
gtk_widget_set_size_request(dl_lb, w, h);
|
||||
pango_layout_get_pixel_size(pango_layout, &width, &height);
|
||||
gtk_widget_set_size_request(ul_lb, width, height);
|
||||
gtk_widget_set_size_request(dl_lb, width, height);
|
||||
g_object_set(ul_lb, "halign", GTK_ALIGN_END, "valign", GTK_ALIGN_CENTER, NULL);
|
||||
g_object_set(dl_lb, "halign", GTK_ALIGN_END, "valign", GTK_ALIGN_CENTER, NULL);
|
||||
g_object_unref(G_OBJECT(pango_layout));
|
||||
|
|
|
@ -374,8 +374,8 @@ char const* gtr_get_help_uri(void)
|
|||
|
||||
if (uri == NULL)
|
||||
{
|
||||
char const* fmt = "https://transmissionbt.com/help/gtk/%d.%dx";
|
||||
uri = g_strdup_printf(fmt, MAJOR_VERSION, MINOR_VERSION / 10);
|
||||
uri = g_strdup_printf("https://transmissionbt.com/help/gtk/%d.%dx",
|
||||
MAJOR_VERSION, MINOR_VERSION / 10);
|
||||
}
|
||||
|
||||
return uri;
|
||||
|
|
|
@ -1,20 +1,11 @@
|
|||
project(libtr)
|
||||
|
||||
configure_file(version.h.in version.h)
|
||||
configure_file(
|
||||
version.h.in
|
||||
version.h
|
||||
)
|
||||
|
||||
set(THIRD_PARTY_SOURCES ConvertUTF.c jsonsl.c wildmat.c)
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
||||
set(DISABLE_WARNINGS -w)
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
set(DISABLE_WARNINGS -w)
|
||||
elseif(CMAKE_C_COMPILER_ID MATCHES "SunPro")
|
||||
set(DISABLE_WARNINGS -erroff)
|
||||
elseif(MSVC)
|
||||
set(DISABLE_WARNINGS /w)
|
||||
endif()
|
||||
set_source_files_properties(${THIRD_PARTY_SOURCES} PROPERTIES COMPILE_FLAGS ${DISABLE_WARNINGS})
|
||||
|
||||
set(${PROJECT_NAME}_SOURCES
|
||||
set(PROJECT_FILES
|
||||
announcer.c
|
||||
announcer-http.c
|
||||
announcer-udp.c
|
||||
|
@ -24,7 +15,6 @@ set(${PROJECT_NAME}_SOURCES
|
|||
cache.c
|
||||
clients.c
|
||||
completion.c
|
||||
ConvertUTF.c
|
||||
crypto.c
|
||||
crypto-utils.c
|
||||
crypto-utils-cyassl.c
|
||||
|
@ -85,9 +75,24 @@ set(${PROJECT_NAME}_SOURCES
|
|||
watchdir-win32.c
|
||||
web.c
|
||||
webseed.c
|
||||
)
|
||||
|
||||
string(REPLACE ";" " " C_WARNING_FLAGS_STR "${C_WARNING_FLAGS}")
|
||||
foreach(FILE ${PROJECT_FILES})
|
||||
set_source_files_properties(${FILE} PROPERTIES COMPILE_FLAGS "${C_WARNING_FLAGS_STR}")
|
||||
endforeach()
|
||||
|
||||
set(THIRD_PARTY_FILES
|
||||
ConvertUTF.c
|
||||
jsonsl.c
|
||||
wildmat.c
|
||||
)
|
||||
|
||||
set(${PROJECT_NAME}_SOURCES
|
||||
${PROJECT_FILES}
|
||||
${THIRD_PARTY_FILES}
|
||||
)
|
||||
|
||||
set_source_files_properties(crypto-utils-fallback.c PROPERTIES HEADER_FILE_ONLY ON)
|
||||
foreach(CP cyassl openssl polarssl)
|
||||
if(NOT CP STREQUAL CRYPTO_PKG)
|
||||
|
@ -226,6 +231,10 @@ endif()
|
|||
include_directories(
|
||||
${PROJECT_SOURCE_DIR}
|
||||
${PROJECT_BINARY_DIR}
|
||||
)
|
||||
|
||||
include_directories(
|
||||
SYSTEM
|
||||
${ZLIB_INCLUDE_DIRS}
|
||||
${CRYPTO_INCLUDE_DIRS}
|
||||
${CURL_INCLUDE_DIRS}
|
||||
|
@ -238,11 +247,11 @@ include_directories(
|
|||
)
|
||||
|
||||
if(ICONV_FOUND)
|
||||
include_directories(${ICONV_INCLUDE_DIRS})
|
||||
include_directories(SYSTEM ${ICONV_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
if(ENABLE_UTP)
|
||||
include_directories(${TP_TOP}/libutp)
|
||||
include_directories(SYSTEM ${TP_TOP}/libutp)
|
||||
endif()
|
||||
|
||||
add_library(${TR_NAME} STATIC
|
||||
|
@ -283,49 +292,3 @@ endif()
|
|||
if(WIN32)
|
||||
target_link_libraries(${TR_NAME} crypt32 shlwapi)
|
||||
endif()
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
add_library(${TR_NAME}-test STATIC
|
||||
libtransmission-test.c
|
||||
libtransmission-test.h
|
||||
)
|
||||
|
||||
target_link_libraries(${TR_NAME}-test ${TR_NAME})
|
||||
set_property(TARGET ${TR_NAME}-test PROPERTY FOLDER "UnitTests")
|
||||
|
||||
set(crypto-test_ADD_SOURCES crypto-test-ref.h)
|
||||
set(subprocess-test_ADD_SOURCES subprocess-test.cmd)
|
||||
|
||||
set(watchdir@generic-test_DEFINITIONS WATCHDIR_TEST_FORCE_GENERIC)
|
||||
|
||||
foreach(T bitfield blocklist clients crypto error file history json magnet makemeta metainfo move peer-msgs quark rename rpc
|
||||
session subprocess tr-getopt utils variant watchdir watchdir@generic)
|
||||
set(TP ${TR_NAME}-test-${T})
|
||||
if(T MATCHES "^([^@]+)@.+$")
|
||||
string(REPLACE "@" "-" TP "${TP}")
|
||||
string(REPLACE "@" "-" T_NAME "${T}")
|
||||
set(${TP}_TEST_BASENAME "${CMAKE_MATCH_1}")
|
||||
else()
|
||||
set(T_NAME "${T}")
|
||||
set(${TP}_TEST_BASENAME "${T}")
|
||||
endif()
|
||||
add_executable(${TP} ${${TP}_TEST_BASENAME}-test.c ${${T}-test_ADD_SOURCES})
|
||||
target_link_libraries(${TP} ${TR_NAME} ${TR_NAME}-test)
|
||||
if(DEFINED ${T}-test_DEFINITIONS)
|
||||
target_compile_definitions(${TP} PRIVATE ${${T}-test_DEFINITIONS})
|
||||
endif()
|
||||
add_test(NAME ${T_NAME}-test COMMAND ${TP})
|
||||
set_property(TARGET ${TP} PROPERTY FOLDER "UnitTests")
|
||||
endforeach()
|
||||
|
||||
if(WIN32)
|
||||
add_custom_command(TARGET ${TR_NAME}-test-subprocess PRE_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_SOURCE_DIR}/subprocess-test.cmd
|
||||
$<TARGET_FILE_DIR:${TR_NAME}-test-subprocess>/${TR_NAME}-test-subprocess.cmd)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(INSTALL_LIB)
|
||||
install(TARGETS ${TR_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(FILES ${${PROJECT_NAME}_PUBLIC_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${TR_NAME})
|
||||
endif()
|
||||
|
|
|
@ -37,6 +37,7 @@ libtransmission_a_SOURCES = \
|
|||
handshake.c \
|
||||
history.c \
|
||||
inout.c \
|
||||
jsonsl.c \
|
||||
list.c \
|
||||
log.c \
|
||||
magnet.c \
|
||||
|
@ -133,7 +134,6 @@ noinst_HEADERS = \
|
|||
inout.h \
|
||||
jsonsl.c \
|
||||
jsonsl.h \
|
||||
libtransmission-test.h \
|
||||
list.h \
|
||||
log.h \
|
||||
magnet.h \
|
||||
|
@ -180,31 +180,6 @@ noinst_HEADERS = \
|
|||
web.h \
|
||||
webseed.h
|
||||
|
||||
TESTS = \
|
||||
bitfield-test \
|
||||
blocklist-test \
|
||||
clients-test \
|
||||
crypto-test \
|
||||
error-test \
|
||||
file-test \
|
||||
history-test \
|
||||
json-test \
|
||||
magnet-test \
|
||||
makemeta-test \
|
||||
metainfo-test \
|
||||
move-test \
|
||||
peer-msgs-test \
|
||||
quark-test \
|
||||
rename-test \
|
||||
rpc-test \
|
||||
session-test \
|
||||
subprocess-test \
|
||||
tr-getopt-test \
|
||||
utils-test \
|
||||
variant-test \
|
||||
watchdir-test \
|
||||
watchdir-generic-test
|
||||
|
||||
noinst_PROGRAMS = $(TESTS)
|
||||
|
||||
apps_ldadd = \
|
||||
|
@ -222,100 +197,3 @@ apps_ldadd = \
|
|||
@ZLIB_LIBS@ \
|
||||
${LIBM}
|
||||
|
||||
TEST_SOURCES = libtransmission-test.c
|
||||
|
||||
bitfield_test_SOURCES = bitfield-test.c $(TEST_SOURCES)
|
||||
bitfield_test_LDADD = ${apps_ldadd}
|
||||
bitfield_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
blocklist_test_SOURCES = blocklist-test.c $(TEST_SOURCES)
|
||||
blocklist_test_LDADD = ${apps_ldadd}
|
||||
blocklist_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
clients_test_SOURCES = clients-test.c $(TEST_SOURCES)
|
||||
clients_test_LDADD = ${apps_ldadd}
|
||||
clients_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
crypto_test_SOURCES = crypto-test.c crypto-test-ref.h $(TEST_SOURCES)
|
||||
crypto_test_LDADD = ${apps_ldadd}
|
||||
crypto_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
error_test_SOURCES = error-test.c $(TEST_SOURCES)
|
||||
error_test_LDADD = ${apps_ldadd}
|
||||
error_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
file_test_SOURCES = file-test.c $(TEST_SOURCES)
|
||||
file_test_LDADD = ${apps_ldadd}
|
||||
file_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
history_test_SOURCES = history-test.c $(TEST_SOURCES)
|
||||
history_test_LDADD = ${apps_ldadd}
|
||||
history_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
json_test_SOURCES = json-test.c $(TEST_SOURCES)
|
||||
json_test_LDADD = ${apps_ldadd}
|
||||
json_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
quark_test_SOURCES = quark-test.c $(TEST_SOURCES)
|
||||
quark_test_LDADD = ${apps_ldadd}
|
||||
quark_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
magnet_test_SOURCES = magnet-test.c $(TEST_SOURCES)
|
||||
magnet_test_LDADD = ${apps_ldadd}
|
||||
magnet_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
metainfo_test_SOURCES = metainfo-test.c $(TEST_SOURCES)
|
||||
metainfo_test_LDADD = ${apps_ldadd}
|
||||
metainfo_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
makemeta_test_SOURCES = makemeta-test.c $(TEST_SOURCES)
|
||||
makemeta_test_LDADD = ${apps_ldadd}
|
||||
makemeta_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
move_test_SOURCES = move-test.c $(TEST_SOURCES)
|
||||
move_test_LDADD = ${apps_ldadd}
|
||||
move_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
peer_msgs_test_SOURCES = peer-msgs-test.c $(TEST_SOURCES)
|
||||
peer_msgs_test_LDADD = ${apps_ldadd}
|
||||
peer_msgs_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
rpc_test_SOURCES = rpc-test.c $(TEST_SOURCES)
|
||||
rpc_test_LDADD = ${apps_ldadd}
|
||||
rpc_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
session_test_SOURCES = session-test.c $(TEST_SOURCES)
|
||||
session_test_LDADD = ${apps_ldadd}
|
||||
session_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
subprocess_test_SOURCES = subprocess-test.c $(TEST_SOURCES)
|
||||
subprocess_test_LDADD = ${apps_ldadd}
|
||||
subprocess_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
tr_getopt_test_SOURCES = tr-getopt-test.c $(TEST_SOURCES)
|
||||
tr_getopt_test_LDADD = ${apps_ldadd}
|
||||
tr_getopt_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
utils_test_SOURCES = utils-test.c $(TEST_SOURCES)
|
||||
utils_test_LDADD = ${apps_ldadd}
|
||||
utils_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
variant_test_SOURCES = variant-test.c $(TEST_SOURCES)
|
||||
variant_test_LDADD = ${apps_ldadd}
|
||||
variant_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
watchdir_test_SOURCES = watchdir-test.c $(TEST_SOURCES)
|
||||
watchdir_test_LDADD = ${apps_ldadd}
|
||||
watchdir_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
watchdir_generic_test_SOURCES = watchdir-test.c $(TEST_SOURCES)
|
||||
watchdir_generic_test_LDADD = ${apps_ldadd}
|
||||
watchdir_generic_test_LDFLAGS = ${apps_ldflags}
|
||||
watchdir_generic_test_CPPFLAGS = -DWATCHDIR_TEST_FORCE_GENERIC $(AM_CPPFLAGS)
|
||||
|
||||
rename_test_SOURCES = rename-test.c $(TEST_SOURCES)
|
||||
rename_test_LDADD = ${apps_ldadd}
|
||||
rename_test_LDFLAGS = ${apps_ldflags}
|
||||
|
||||
EXTRA_DIST = \
|
||||
subprocess-test.cmd
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef __LIBTRANSMISSION_ANNOUNCER_MODULE__
|
||||
#ifndef LIBTRANSMISSION_ANNOUNCER_MODULE
|
||||
#error only the libtransmission announcer module should #include this header.
|
||||
#endif
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <event2/buffer.h>
|
||||
#include <event2/http.h> /* for HTTP_OK */
|
||||
|
||||
#define __LIBTRANSMISSION_ANNOUNCER_MODULE__
|
||||
#define LIBTRANSMISSION_ANNOUNCER_MODULE
|
||||
|
||||
#include "transmission.h"
|
||||
#include "announcer-common.h"
|
||||
|
@ -396,8 +396,6 @@ static void on_scrape_done(tr_session* session, bool did_connect, bool did_timeo
|
|||
int64_t intVal;
|
||||
tr_variant* files;
|
||||
tr_variant* flags;
|
||||
size_t len;
|
||||
char const* str;
|
||||
bool const variant_loaded = tr_variantFromBenc(&top, msg, msglen) == 0;
|
||||
|
||||
if (tr_env_key_exists("TR_CURL_VERBOSE"))
|
||||
|
@ -424,6 +422,8 @@ static void on_scrape_done(tr_session* session, bool did_connect, bool did_timeo
|
|||
|
||||
if (variant_loaded)
|
||||
{
|
||||
size_t len;
|
||||
char const* str;
|
||||
if (tr_variantDictFindStr(&top, TR_KEY_failure_reason, &str, &len))
|
||||
{
|
||||
response->errmsg = tr_strndup(str, len);
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <event2/dns.h>
|
||||
#include <event2/util.h>
|
||||
|
||||
#define __LIBTRANSMISSION_ANNOUNCER_MODULE__
|
||||
#define LIBTRANSMISSION_ANNOUNCER_MODULE
|
||||
|
||||
#include "transmission.h"
|
||||
#include "announcer.h"
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <event2/buffer.h>
|
||||
#include <event2/event.h> /* evtimer */
|
||||
|
||||
#define __LIBTRANSMISSION_ANNOUNCER_MODULE__
|
||||
#define LIBTRANSMISSION_ANNOUNCER_MODULE
|
||||
|
||||
#include "transmission.h"
|
||||
#include "announcer.h"
|
||||
|
|
|
@ -1,244 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2010-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h> /* strlen() */
|
||||
#include "transmission.h"
|
||||
#include "crypto-utils.h"
|
||||
#include "bitfield.h"
|
||||
#include "utils.h" /* tr_free */
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
static int test_bitfield_count_range(void)
|
||||
{
|
||||
int begin;
|
||||
int end;
|
||||
int count1;
|
||||
int count2;
|
||||
int const bitCount = 100 + tr_rand_int_weak(1000);
|
||||
tr_bitfield bf;
|
||||
|
||||
/* generate a random bitfield */
|
||||
tr_bitfieldConstruct(&bf, bitCount);
|
||||
|
||||
for (int i = 0, n = tr_rand_int_weak(bitCount); i < n; ++i)
|
||||
{
|
||||
tr_bitfieldAdd(&bf, tr_rand_int_weak(bitCount));
|
||||
}
|
||||
|
||||
begin = tr_rand_int_weak(bitCount);
|
||||
|
||||
do
|
||||
{
|
||||
end = tr_rand_int_weak(bitCount);
|
||||
}
|
||||
while (end == begin);
|
||||
|
||||
/* ensure end <= begin */
|
||||
if (end < begin)
|
||||
{
|
||||
int const tmp = begin;
|
||||
begin = end;
|
||||
end = tmp;
|
||||
}
|
||||
|
||||
/* test the bitfield */
|
||||
count1 = 0;
|
||||
|
||||
for (int i = begin; i < end; ++i)
|
||||
{
|
||||
if (tr_bitfieldHas(&bf, i))
|
||||
{
|
||||
++count1;
|
||||
}
|
||||
}
|
||||
|
||||
count2 = tr_bitfieldCountRange(&bf, begin, end);
|
||||
check_int(count1, ==, count2);
|
||||
|
||||
/* cleanup */
|
||||
tr_bitfieldDestruct(&bf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_bitfields(void)
|
||||
{
|
||||
unsigned int bitcount = 500;
|
||||
tr_bitfield field;
|
||||
|
||||
tr_bitfieldConstruct(&field, bitcount);
|
||||
|
||||
/* test tr_bitfieldAdd */
|
||||
for (unsigned int i = 0; i < bitcount; i++)
|
||||
{
|
||||
if (i % 7 == 0)
|
||||
{
|
||||
tr_bitfieldAdd(&field, i);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < bitcount; i++)
|
||||
{
|
||||
check_bool(tr_bitfieldHas(&field, i), ==, (i % 7 == 0));
|
||||
}
|
||||
|
||||
/* test tr_bitfieldAddRange */
|
||||
tr_bitfieldAddRange(&field, 0, bitcount);
|
||||
|
||||
for (unsigned int i = 0; i < bitcount; i++)
|
||||
{
|
||||
check(tr_bitfieldHas(&field, i));
|
||||
}
|
||||
|
||||
/* test tr_bitfieldRem */
|
||||
for (unsigned int i = 0; i < bitcount; i++)
|
||||
{
|
||||
if (i % 7 != 0)
|
||||
{
|
||||
tr_bitfieldRem(&field, i);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < bitcount; i++)
|
||||
{
|
||||
check_bool(tr_bitfieldHas(&field, i), ==, (i % 7 == 0));
|
||||
}
|
||||
|
||||
/* test tr_bitfieldRemRange in the middle of a boundary */
|
||||
tr_bitfieldAddRange(&field, 0, 64);
|
||||
tr_bitfieldRemRange(&field, 4, 21);
|
||||
|
||||
for (unsigned int i = 0; i < 64; i++)
|
||||
{
|
||||
check_bool(tr_bitfieldHas(&field, i), ==, (i < 4 || i >= 21));
|
||||
}
|
||||
|
||||
/* test tr_bitfieldRemRange on the boundaries */
|
||||
tr_bitfieldAddRange(&field, 0, 64);
|
||||
tr_bitfieldRemRange(&field, 8, 24);
|
||||
|
||||
for (unsigned int i = 0; i < 64; i++)
|
||||
{
|
||||
check_bool(tr_bitfieldHas(&field, i), ==, (i < 8 || i >= 24));
|
||||
}
|
||||
|
||||
/* test tr_bitfieldRemRange when begin & end is on the same word */
|
||||
tr_bitfieldAddRange(&field, 0, 64);
|
||||
tr_bitfieldRemRange(&field, 4, 5);
|
||||
|
||||
for (unsigned int i = 0; i < 64; i++)
|
||||
{
|
||||
check_bool(tr_bitfieldHas(&field, i), ==, (i < 4 || i >= 5));
|
||||
}
|
||||
|
||||
/* test tr_bitfieldAddRange */
|
||||
tr_bitfieldRemRange(&field, 0, 64);
|
||||
tr_bitfieldAddRange(&field, 4, 21);
|
||||
|
||||
for (unsigned int i = 0; i < 64; i++)
|
||||
{
|
||||
check_bool(tr_bitfieldHas(&field, i), ==, (4 <= i && i < 21));
|
||||
}
|
||||
|
||||
/* test tr_bitfieldAddRange on the boundaries */
|
||||
tr_bitfieldRemRange(&field, 0, 64);
|
||||
tr_bitfieldAddRange(&field, 8, 24);
|
||||
|
||||
for (unsigned int i = 0; i < 64; i++)
|
||||
{
|
||||
check_bool(tr_bitfieldHas(&field, i), ==, (8 <= i && i < 24));
|
||||
}
|
||||
|
||||
/* test tr_bitfieldAddRange when begin & end is on the same word */
|
||||
tr_bitfieldRemRange(&field, 0, 64);
|
||||
tr_bitfieldAddRange(&field, 4, 5);
|
||||
|
||||
for (unsigned int i = 0; i < 64; i++)
|
||||
{
|
||||
check_bool(tr_bitfieldHas(&field, i), ==, (4 <= i && i < 5));
|
||||
}
|
||||
|
||||
tr_bitfieldDestruct(&field);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_bitfield_has_all_none(void)
|
||||
{
|
||||
tr_bitfield field;
|
||||
|
||||
tr_bitfieldConstruct(&field, 3);
|
||||
|
||||
check(!tr_bitfieldHasAll(&field));
|
||||
check(tr_bitfieldHasNone(&field));
|
||||
|
||||
tr_bitfieldAdd(&field, 0);
|
||||
check(!tr_bitfieldHasAll(&field));
|
||||
check(!tr_bitfieldHasNone(&field));
|
||||
|
||||
tr_bitfieldRem(&field, 0);
|
||||
tr_bitfieldAdd(&field, 1);
|
||||
check(!tr_bitfieldHasAll(&field));
|
||||
check(!tr_bitfieldHasNone(&field));
|
||||
|
||||
tr_bitfieldRem(&field, 1);
|
||||
tr_bitfieldAdd(&field, 2);
|
||||
check(!tr_bitfieldHasAll(&field));
|
||||
check(!tr_bitfieldHasNone(&field));
|
||||
|
||||
tr_bitfieldAdd(&field, 0);
|
||||
tr_bitfieldAdd(&field, 1);
|
||||
check(tr_bitfieldHasAll(&field));
|
||||
check(!tr_bitfieldHasNone(&field));
|
||||
|
||||
tr_bitfieldSetHasNone(&field);
|
||||
check(!tr_bitfieldHasAll(&field));
|
||||
check(tr_bitfieldHasNone(&field));
|
||||
|
||||
tr_bitfieldSetHasAll(&field);
|
||||
check(tr_bitfieldHasAll(&field));
|
||||
check(!tr_bitfieldHasNone(&field));
|
||||
|
||||
tr_bitfieldDestruct(&field);
|
||||
tr_bitfieldConstruct(&field, 0);
|
||||
|
||||
check(!tr_bitfieldHasAll(&field));
|
||||
check(!tr_bitfieldHasNone(&field));
|
||||
|
||||
tr_bitfieldSetHasNone(&field);
|
||||
check(!tr_bitfieldHasAll(&field));
|
||||
check(tr_bitfieldHasNone(&field));
|
||||
|
||||
tr_bitfieldSetHasAll(&field);
|
||||
check(tr_bitfieldHasAll(&field));
|
||||
check(!tr_bitfieldHasNone(&field));
|
||||
|
||||
tr_bitfieldDestruct(&field);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testFunc const tests[] =
|
||||
{
|
||||
test_bitfields,
|
||||
test_bitfield_has_all_none
|
||||
};
|
||||
|
||||
int ret = runTests(tests, NUM_TESTS(tests));
|
||||
|
||||
/* bitfield count range */
|
||||
for (int l = 0; l < 10000; ++l)
|
||||
{
|
||||
if (test_bitfield_count_range() != 0)
|
||||
{
|
||||
++ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -13,6 +13,9 @@
|
|||
#endif
|
||||
|
||||
#include "transmission.h"
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
/** @brief Implementation of the BitTorrent spec's Bitfield array of bits */
|
||||
typedef struct tr_bitfield
|
||||
|
@ -91,3 +94,5 @@ static inline bool tr_bitfieldHasNone(tr_bitfield const* b)
|
|||
}
|
||||
|
||||
bool tr_bitfieldHas(tr_bitfield const* b, size_t n);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,159 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h> /* strlen() */
|
||||
|
||||
#include "transmission.h"
|
||||
#include "blocklist.h"
|
||||
#include "file.h"
|
||||
#include "net.h"
|
||||
#include "session.h" /* tr_sessionIsAddressBlocked() */
|
||||
#include "utils.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
static char const* contents1 =
|
||||
"10.5.6.7/8\n"
|
||||
"Austin Law Firm:216.16.1.144-216.16.1.151\n"
|
||||
"Sargent Controls and Aerospace:216.19.18.0-216.19.18.255\n"
|
||||
"Corel Corporation:216.21.157.192-216.21.157.223\n"
|
||||
"Fox Speed Channel:216.79.131.192-216.79.131.223\n";
|
||||
|
||||
static char const* contents2 =
|
||||
"10.5.6.7/8\n"
|
||||
"Austin Law Firm:216.16.1.144-216.16.1.151\n"
|
||||
"Sargent Controls and Aerospace:216.19.18.0-216.19.18.255\n"
|
||||
"Corel Corporation:216.21.157.192-216.21.157.223\n"
|
||||
"Fox Speed Channel:216.79.131.192-216.79.131.223\n"
|
||||
"Evilcorp:216.88.88.0-216.88.88.255\n";
|
||||
|
||||
static void create_text_file(char const* path, char const* contents)
|
||||
{
|
||||
tr_sys_file_t fd;
|
||||
char* dir;
|
||||
|
||||
dir = tr_sys_path_dirname(path, NULL);
|
||||
tr_sys_dir_create(dir, TR_SYS_DIR_CREATE_PARENTS, 0700, NULL);
|
||||
tr_free(dir);
|
||||
|
||||
fd = tr_sys_file_open(path, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_TRUNCATE, 0600, NULL);
|
||||
tr_sys_file_write(fd, contents, strlen(contents), NULL, NULL);
|
||||
tr_sys_file_close(fd, NULL);
|
||||
|
||||
libttest_sync();
|
||||
}
|
||||
|
||||
static bool address_is_blocked(tr_session* session, char const* address_str)
|
||||
{
|
||||
struct tr_address addr;
|
||||
tr_address_from_string(&addr, address_str);
|
||||
return tr_sessionIsAddressBlocked(session, &addr);
|
||||
}
|
||||
|
||||
static int test_parsing(void)
|
||||
{
|
||||
char* path;
|
||||
tr_session* session;
|
||||
|
||||
/* init the session */
|
||||
session = libttest_session_init(NULL);
|
||||
check(!tr_blocklistExists(session));
|
||||
check_int(tr_blocklistGetRuleCount(session), ==, 0);
|
||||
|
||||
/* init the blocklist */
|
||||
path = tr_buildPath(tr_sessionGetConfigDir(session), "blocklists", "level1", NULL);
|
||||
create_text_file(path, contents1);
|
||||
tr_free(path);
|
||||
tr_sessionReloadBlocklists(session);
|
||||
check(tr_blocklistExists(session));
|
||||
check_int(tr_blocklistGetRuleCount(session), ==, 5);
|
||||
|
||||
/* enable the blocklist */
|
||||
check(!tr_blocklistIsEnabled(session));
|
||||
tr_blocklistSetEnabled(session, true);
|
||||
check(tr_blocklistIsEnabled(session));
|
||||
|
||||
/* test blocked addresses */
|
||||
check(!address_is_blocked(session, "0.0.0.1"));
|
||||
check(address_is_blocked(session, "10.1.2.3"));
|
||||
check(!address_is_blocked(session, "216.16.1.143"));
|
||||
check(address_is_blocked(session, "216.16.1.144"));
|
||||
check(address_is_blocked(session, "216.16.1.145"));
|
||||
check(address_is_blocked(session, "216.16.1.146"));
|
||||
check(address_is_blocked(session, "216.16.1.147"));
|
||||
check(address_is_blocked(session, "216.16.1.148"));
|
||||
check(address_is_blocked(session, "216.16.1.149"));
|
||||
check(address_is_blocked(session, "216.16.1.150"));
|
||||
check(address_is_blocked(session, "216.16.1.151"));
|
||||
check(!address_is_blocked(session, "216.16.1.152"));
|
||||
check(!address_is_blocked(session, "216.16.1.153"));
|
||||
check(!address_is_blocked(session, "217.0.0.1"));
|
||||
check(!address_is_blocked(session, "255.0.0.1"));
|
||||
|
||||
/* cleanup */
|
||||
libttest_session_close(session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static int test_updating(void)
|
||||
{
|
||||
char* path;
|
||||
tr_session* session;
|
||||
|
||||
/* init the session */
|
||||
session = libttest_session_init(NULL);
|
||||
path = tr_buildPath(tr_sessionGetConfigDir(session), "blocklists", "level1", NULL);
|
||||
|
||||
/* no blocklist to start with... */
|
||||
check_int(tr_blocklistGetRuleCount(session), ==, 0);
|
||||
|
||||
/* test that updated source files will get loaded */
|
||||
create_text_file(path, contents1);
|
||||
tr_sessionReloadBlocklists(session);
|
||||
check_int(tr_blocklistGetRuleCount(session), ==, 5);
|
||||
|
||||
/* test that updated source files will get loaded */
|
||||
create_text_file(path, contents2);
|
||||
tr_sessionReloadBlocklists(session);
|
||||
check_int(tr_blocklistGetRuleCount(session), ==, 6);
|
||||
|
||||
/* test that updated source files will get loaded */
|
||||
create_text_file(path, contents1);
|
||||
tr_sessionReloadBlocklists(session);
|
||||
check_int(tr_blocklistGetRuleCount(session), ==, 5);
|
||||
|
||||
/* ensure that new files, if bad, get skipped */
|
||||
create_text_file(path, "# nothing useful\n");
|
||||
tr_sessionReloadBlocklists(session);
|
||||
check_int(tr_blocklistGetRuleCount(session), ==, 5);
|
||||
|
||||
/* cleanup */
|
||||
libttest_session_close(session);
|
||||
tr_free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testFunc const tests[] =
|
||||
{
|
||||
test_parsing,
|
||||
test_updating
|
||||
};
|
||||
|
||||
return runTests(tests, NUM_TESTS(tests));
|
||||
}
|
|
@ -12,6 +12,10 @@
|
|||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
struct tr_address;
|
||||
|
||||
typedef struct tr_blocklistFile tr_blocklistFile;
|
||||
|
@ -33,3 +37,5 @@ void tr_blocklistFileSetEnabled(tr_blocklistFile* b, bool isEnabled);
|
|||
bool tr_blocklistFileHasAddress(tr_blocklistFile* b, struct tr_address const* addr);
|
||||
|
||||
int tr_blocklistFileSetContent(tr_blocklistFile* b, char const* filename);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
struct evbuffer;
|
||||
|
||||
typedef struct tr_cache tr_cache;
|
||||
|
@ -49,3 +53,5 @@ int tr_cacheFlushDone(tr_cache* cache);
|
|||
int tr_cacheFlushTorrent(tr_cache* cache, tr_torrent* torrent);
|
||||
|
||||
int tr_cacheFlushFile(tr_cache* cache, tr_torrent* torrent, tr_file_index_t file);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "transmission.h"
|
||||
#include "clients.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
#define TEST_CLIENT(A, B) \
|
||||
tr_clientForId(buf, sizeof(buf), A); \
|
||||
check_str(buf, ==, B);
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
TEST_CLIENT("-FC1013-", "FileCroc 1.0.1.3");
|
||||
TEST_CLIENT("-MR1100-", "Miro 1.1.0.0");
|
||||
TEST_CLIENT("-TR0006-", "Transmission 0.6");
|
||||
TEST_CLIENT("-TR0072-", "Transmission 0.72");
|
||||
TEST_CLIENT("-TR111Z-", "Transmission 1.11+");
|
||||
TEST_CLIENT("O1008132", "Osprey 1.0.0");
|
||||
TEST_CLIENT("TIX0193-", "Tixati 1.93");
|
||||
TEST_CLIENT("-UT341\0-", "\xc2\xb5Torrent 3.4.1");
|
||||
TEST_CLIENT("-BT791\0-", "BitTorrent 7.9.1");
|
||||
TEST_CLIENT("-BT791B-", "BitTorrent 7.9.1 (Beta)");
|
||||
|
||||
/* Xfplay 9.9.92 to 9.9.94 uses "-XF9992-" */
|
||||
TEST_CLIENT("-XF9992-", "Xfplay 9.9.92");
|
||||
|
||||
/* Older Xfplay versions have three digit version number */
|
||||
TEST_CLIENT("-XF9990-", "Xfplay 9.9.9");
|
||||
|
||||
/* PicoTorrent */
|
||||
TEST_CLIENT("-PI0091-", "PicoTorrent 0.09.1");
|
||||
TEST_CLIENT("-PI0120-", "PicoTorrent 0.12.0");
|
||||
|
||||
/* Free Download Manager */
|
||||
TEST_CLIENT("-FD51R\xFF-", "Free Download Manager 5.1.27");
|
||||
TEST_CLIENT("-FD51W\xFF-", "Free Download Manager 5.1.32");
|
||||
TEST_CLIENT("-FD51@\xFF-", "Free Download Manager 5.1.x"); /* Negative test case */
|
||||
|
||||
/* Folx */
|
||||
TEST_CLIENT("-FL51FF-", "Folx 5.x"); /* Folx v5.2.1.13690 */
|
||||
|
||||
/* Baidu Netdisk */
|
||||
TEST_CLIENT("-BN0001-", "Baidu Netdisk"); /* Baidu Netdisk Client v5.5.4 */
|
||||
|
||||
/* gobbledygook */
|
||||
TEST_CLIENT("-IIO\x10\x2D\x04-", "-IIO%10-%04-");
|
||||
TEST_CLIENT("-I\05O\x08\x03\x01-", "-I%05O%08%03%01-");
|
||||
|
||||
TEST_CLIENT("\x65\x78\x62\x63\x00\x38\x7A\x44\x63\x10\x2D\x6E\x9A\xD6\x72\x3B\x33\x9F\x35\xA9", "BitComet 0.56");
|
||||
TEST_CLIENT("\x65\x78\x62\x63\x00\x38\x4C\x4F\x52\x44\x32\x00\x04\x8E\xCE\xD5\x7B\xD7\x10\x28", "BitLord 0.56");
|
||||
|
||||
/* cleanup */
|
||||
return 0;
|
||||
}
|
|
@ -12,8 +12,14 @@
|
|||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* @brief parse a peer-id into a human-readable client name and version number
|
||||
* @ingroup utils
|
||||
*/
|
||||
char* tr_clientForId(char* buf, size_t buflen, void const* peer_id);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,317 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "transmission.h"
|
||||
#include "crypto.h"
|
||||
#include "crypto-utils.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
#include "crypto-test-ref.h"
|
||||
|
||||
static int test_torrent_hash(void)
|
||||
{
|
||||
tr_crypto a;
|
||||
uint8_t hash[SHA_DIGEST_LENGTH];
|
||||
|
||||
for (uint8_t i = 0; i < SHA_DIGEST_LENGTH; ++i)
|
||||
{
|
||||
hash[i] = i;
|
||||
}
|
||||
|
||||
tr_cryptoConstruct(&a, NULL, true);
|
||||
|
||||
check(!tr_cryptoHasTorrentHash(&a));
|
||||
check_ptr(tr_cryptoGetTorrentHash(&a), ==, NULL);
|
||||
|
||||
tr_cryptoSetTorrentHash(&a, hash);
|
||||
check(tr_cryptoHasTorrentHash(&a));
|
||||
check_ptr(tr_cryptoGetTorrentHash(&a), !=, NULL);
|
||||
check_mem(tr_cryptoGetTorrentHash(&a), ==, hash, SHA_DIGEST_LENGTH);
|
||||
|
||||
tr_cryptoDestruct(&a);
|
||||
|
||||
for (uint8_t i = 0; i < SHA_DIGEST_LENGTH; ++i)
|
||||
{
|
||||
hash[i] = i + 1;
|
||||
}
|
||||
|
||||
tr_cryptoConstruct(&a, hash, false);
|
||||
|
||||
check(tr_cryptoHasTorrentHash(&a));
|
||||
check_ptr(tr_cryptoGetTorrentHash(&a), !=, NULL);
|
||||
check_mem(tr_cryptoGetTorrentHash(&a), ==, hash, SHA_DIGEST_LENGTH);
|
||||
|
||||
tr_cryptoSetTorrentHash(&a, NULL);
|
||||
check(!tr_cryptoHasTorrentHash(&a));
|
||||
check_ptr(tr_cryptoGetTorrentHash(&a), ==, NULL);
|
||||
|
||||
tr_cryptoDestruct(&a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_encrypt_decrypt(void)
|
||||
{
|
||||
tr_crypto a;
|
||||
tr_crypto_ b;
|
||||
uint8_t hash[SHA_DIGEST_LENGTH];
|
||||
char const test1[] = { "test1" };
|
||||
char buf11[sizeof(test1)];
|
||||
char buf12[sizeof(test1)];
|
||||
char const test2[] = { "@#)C$@)#(*%bvkdjfhwbc039bc4603756VB3)" };
|
||||
char buf21[sizeof(test2)];
|
||||
char buf22[sizeof(test2)];
|
||||
int public_key_length;
|
||||
|
||||
for (int i = 0; i < SHA_DIGEST_LENGTH; ++i)
|
||||
{
|
||||
hash[i] = (uint8_t)i;
|
||||
}
|
||||
|
||||
tr_cryptoConstruct(&a, hash, false);
|
||||
tr_cryptoConstruct_(&b, hash, true);
|
||||
check(tr_cryptoComputeSecret(&a, tr_cryptoGetMyPublicKey_(&b, &public_key_length)));
|
||||
check(tr_cryptoComputeSecret_(&b, tr_cryptoGetMyPublicKey(&a, &public_key_length)));
|
||||
|
||||
tr_cryptoEncryptInit(&a);
|
||||
tr_cryptoEncrypt(&a, sizeof(test1), test1, buf11);
|
||||
tr_cryptoDecryptInit_(&b);
|
||||
tr_cryptoDecrypt_(&b, sizeof(test1), buf11, buf12);
|
||||
check_str(buf12, ==, test1);
|
||||
|
||||
tr_cryptoEncryptInit_(&b);
|
||||
tr_cryptoEncrypt_(&b, sizeof(test2), test2, buf21);
|
||||
tr_cryptoDecryptInit(&a);
|
||||
tr_cryptoDecrypt(&a, sizeof(test2), buf21, buf22);
|
||||
check_str(buf22, ==, test2);
|
||||
|
||||
tr_cryptoDestruct_(&b);
|
||||
tr_cryptoDestruct(&a);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_sha1(void)
|
||||
{
|
||||
uint8_t hash[SHA_DIGEST_LENGTH];
|
||||
uint8_t hash_[SHA_DIGEST_LENGTH];
|
||||
|
||||
check(tr_sha1(hash, "test", 4, NULL));
|
||||
check(tr_sha1_(hash_, "test", 4, NULL));
|
||||
check_mem(hash, ==, "\xa9\x4a\x8f\xe5\xcc\xb1\x9b\xa6\x1c\x4c\x08\x73\xd3\x91\xe9\x87\x98\x2f\xbb\xd3", SHA_DIGEST_LENGTH);
|
||||
check_mem(hash, ==, hash_, SHA_DIGEST_LENGTH);
|
||||
|
||||
check(tr_sha1(hash, "1", 1, "22", 2, "333", 3, NULL));
|
||||
check(tr_sha1_(hash_, "1", 1, "22", 2, "333", 3, NULL));
|
||||
check_mem(hash, ==, "\x1f\x74\x64\x8e\x50\xa6\xa6\x70\x8e\xc5\x4a\xb3\x27\xa1\x63\xd5\x53\x6b\x7c\xed", SHA_DIGEST_LENGTH);
|
||||
check_mem(hash, ==, hash_, SHA_DIGEST_LENGTH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_ssha1(void)
|
||||
{
|
||||
struct
|
||||
{
|
||||
char const* const plain_text;
|
||||
char const* const ssha1;
|
||||
}
|
||||
test_data[] =
|
||||
{
|
||||
{ "test", "{15ad0621b259a84d24dcd4e75b09004e98a3627bAMbyRHJy" },
|
||||
{ "QNY)(*#$B)!_X$B !_B#($^!)*&$%CV!#)&$C!@$(P*)", "{10e2d7acbb104d970514a147cd16d51dfa40fb3c0OSwJtOL" }
|
||||
};
|
||||
|
||||
#define HASH_COUNT (4 * 1024)
|
||||
|
||||
for (size_t i = 0; i < TR_N_ELEMENTS(test_data); ++i)
|
||||
{
|
||||
char* const phrase = tr_strdup(test_data[i].plain_text);
|
||||
char** hashes = tr_new(char*, HASH_COUNT);
|
||||
|
||||
check(tr_ssha1_matches(test_data[i].ssha1, phrase));
|
||||
check(tr_ssha1_matches_(test_data[i].ssha1, phrase));
|
||||
|
||||
for (size_t j = 0; j < HASH_COUNT; ++j)
|
||||
{
|
||||
hashes[j] = j % 2 == 0 ? tr_ssha1(phrase) : tr_ssha1_(phrase);
|
||||
|
||||
check_ptr(hashes[j], !=, NULL);
|
||||
|
||||
/* phrase matches each of generated hashes */
|
||||
check(tr_ssha1_matches(hashes[j], phrase));
|
||||
check(tr_ssha1_matches_(hashes[j], phrase));
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < HASH_COUNT; ++j)
|
||||
{
|
||||
/* all hashes are different */
|
||||
for (size_t k = 0; k < HASH_COUNT; ++k)
|
||||
{
|
||||
if (k != j)
|
||||
{
|
||||
check_str(hashes[j], !=, hashes[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* exchange two first chars */
|
||||
phrase[0] ^= phrase[1];
|
||||
phrase[1] ^= phrase[0];
|
||||
phrase[0] ^= phrase[1];
|
||||
|
||||
for (size_t j = 0; j < HASH_COUNT; ++j)
|
||||
{
|
||||
/* changed phrase doesn't match the hashes */
|
||||
check(!tr_ssha1_matches(hashes[j], phrase));
|
||||
check(!tr_ssha1_matches_(hashes[j], phrase));
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < HASH_COUNT; ++j)
|
||||
{
|
||||
tr_free(hashes[j]);
|
||||
}
|
||||
|
||||
tr_free(hashes);
|
||||
tr_free(phrase);
|
||||
}
|
||||
|
||||
#undef HASH_COUNT
|
||||
|
||||
/* should work with different salt lengths as well */
|
||||
check(tr_ssha1_matches("{a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "test"));
|
||||
check(tr_ssha1_matches("{d209a21d3bc4f8fc4f8faf347e69f3def597eb170pySy4ai1ZPMjeU1", "test"));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_random(void)
|
||||
{
|
||||
/* test that tr_rand_int() stays in-bounds */
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
{
|
||||
int const val = tr_rand_int(100);
|
||||
check_int(val, >=, 0);
|
||||
check_int(val, <, 100);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool base64_eq(char const* a, char const* b)
|
||||
{
|
||||
for (;; ++a, ++b)
|
||||
{
|
||||
while (*a == '\r' || *a == '\n')
|
||||
{
|
||||
++a;
|
||||
}
|
||||
|
||||
while (*b == '\r' || *b == '\n')
|
||||
{
|
||||
++b;
|
||||
}
|
||||
|
||||
if (*a == '\0' || *b == '\0' || *a != *b)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
static int test_base64(void)
|
||||
{
|
||||
size_t len;
|
||||
char* in;
|
||||
char* out;
|
||||
|
||||
out = tr_base64_encode_str("YOYO!", &len);
|
||||
check_uint(len, ==, strlen(out));
|
||||
check(base64_eq("WU9ZTyE=", out));
|
||||
in = tr_base64_decode_str(out, &len);
|
||||
check_uint(len, ==, 5);
|
||||
check_str(in, ==, "YOYO!");
|
||||
tr_free(in);
|
||||
tr_free(out);
|
||||
|
||||
out = tr_base64_encode("", 0, &len);
|
||||
check_uint(len, ==, 0);
|
||||
check_str(out, ==, "");
|
||||
tr_free(out);
|
||||
out = tr_base64_decode("", 0, &len);
|
||||
check_uint(len, ==, 0);
|
||||
check_str(out, ==, "");
|
||||
tr_free(out);
|
||||
|
||||
out = tr_base64_encode(NULL, 0, &len);
|
||||
check_uint(len, ==, 0);
|
||||
check_str(out, ==, NULL);
|
||||
out = tr_base64_decode(NULL, 0, &len);
|
||||
check_uint(len, ==, 0);
|
||||
check_str(out, ==, NULL);
|
||||
|
||||
#define MAX_BUF_SIZE 1024
|
||||
|
||||
for (size_t i = 1; i <= MAX_BUF_SIZE; ++i)
|
||||
{
|
||||
char buf[MAX_BUF_SIZE + 1];
|
||||
|
||||
for (size_t j = 0; j < i; ++j)
|
||||
{
|
||||
buf[j] = (char)tr_rand_int_weak(256);
|
||||
}
|
||||
|
||||
out = tr_base64_encode(buf, i, &len);
|
||||
check_uint(len, ==, strlen(out));
|
||||
in = tr_base64_decode(out, len, &len);
|
||||
check_uint(len, ==, i);
|
||||
check_mem(in, ==, buf, len);
|
||||
tr_free(in);
|
||||
tr_free(out);
|
||||
|
||||
for (size_t j = 0; j < i; ++j)
|
||||
{
|
||||
buf[j] = (char)(1 + tr_rand_int_weak(255));
|
||||
}
|
||||
|
||||
buf[i] = '\0';
|
||||
|
||||
out = tr_base64_encode_str(buf, &len);
|
||||
check_uint(len, ==, strlen(out));
|
||||
in = tr_base64_decode_str(out, &len);
|
||||
check_uint(len, ==, i);
|
||||
check_str(buf, ==, in);
|
||||
tr_free(in);
|
||||
tr_free(out);
|
||||
}
|
||||
|
||||
#undef MAX_BUF_SIZE
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testFunc const tests[] =
|
||||
{
|
||||
test_torrent_hash,
|
||||
test_encrypt_decrypt,
|
||||
test_sha1,
|
||||
test_ssha1,
|
||||
test_random,
|
||||
test_base64
|
||||
};
|
||||
|
||||
return runTests(tests, NUM_TESTS(tests));
|
||||
}
|
|
@ -81,7 +81,6 @@ static bool check_openssl_result(int result, int expected_result, bool expected_
|
|||
}
|
||||
|
||||
#define check_result(result) check_openssl_result((result), 1, true, __FILE__, __LINE__)
|
||||
#define check_result_eq(result, x_result) check_openssl_result((result), (x_result), true, __FILE__, __LINE__)
|
||||
#define check_result_neq(result, x_result) check_openssl_result((result), (x_result), false, __FILE__, __LINE__)
|
||||
|
||||
static bool check_openssl_pointer(void* pointer, char const* file, int line)
|
||||
|
|
|
@ -13,12 +13,10 @@
|
|||
#include <stddef.h>
|
||||
|
||||
#include "transmission.h" /* SHA_DIGEST_LENGTH */
|
||||
#include "tr-macros.h"
|
||||
#include "utils.h" /* TR_GNUC_MALLOC, TR_GNUC_NULL_TERMINATED */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
*** @addtogroup utils Utilities
|
||||
|
@ -193,7 +191,7 @@ void* tr_base64_decode_str(char const* input, size_t* output_length) TR_GNUC_MAL
|
|||
/**
|
||||
* @brief Wrapper around tr_binary_to_hex() for SHA_DIGEST_LENGTH.
|
||||
*/
|
||||
static inline void tr_sha1_to_hex(char* hex, uint8_t const* sha1)
|
||||
static inline void tr_sha1_to_hex(void* hex, void const* sha1)
|
||||
{
|
||||
tr_binary_to_hex(sha1, hex, SHA_DIGEST_LENGTH);
|
||||
}
|
||||
|
@ -201,15 +199,13 @@ static inline void tr_sha1_to_hex(char* hex, uint8_t const* sha1)
|
|||
/**
|
||||
* @brief Wrapper around tr_hex_to_binary() for SHA_DIGEST_LENGTH.
|
||||
*/
|
||||
static inline void tr_hex_to_sha1(uint8_t* sha1, char const* hex)
|
||||
static inline void tr_hex_to_sha1(void* sha1, void const* hex)
|
||||
{
|
||||
tr_hex_to_binary(hex, sha1, SHA_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
TR_END_DECLS
|
||||
|
||||
#endif /* TR_CRYPTO_UTILS_H */
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
// NB: crypto-test-ref.h needs this, so use it instead of #pragma once
|
||||
#ifndef TR_ENCRYPTION_H
|
||||
#define TR_ENCRYPTION_H
|
||||
|
||||
|
@ -16,8 +17,11 @@
|
|||
#include <inttypes.h>
|
||||
|
||||
#include "crypto-utils.h"
|
||||
#include "tr-macros.h"
|
||||
#include "utils.h" /* TR_GNUC_NULL_TERMINATED */
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
*** @addtogroup peers
|
||||
*** @{
|
||||
|
@ -71,4 +75,6 @@ bool tr_cryptoSecretKeySha1(tr_crypto const* crypto, void const* prepend_data, s
|
|||
|
||||
/* @} */
|
||||
|
||||
#endif
|
||||
TR_END_DECLS
|
||||
|
||||
#endif // TR_ENCRYPTION_H
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "transmission.h"
|
||||
#include "error.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
static int test_error_set(void)
|
||||
{
|
||||
tr_error* err = NULL;
|
||||
|
||||
tr_error_prefix(&err, "error: ");
|
||||
check_ptr(err, ==, NULL);
|
||||
|
||||
tr_error_set(&err, 1, "error: %s (%d)", "oops", 2);
|
||||
check(err != NULL);
|
||||
check_int(err->code, ==, 1);
|
||||
check_str(err->message, ==, "error: oops (2)");
|
||||
tr_error_clear(&err);
|
||||
check_ptr(err, ==, NULL);
|
||||
|
||||
tr_error_set_literal(&err, 2, "oops");
|
||||
check_ptr(err, !=, NULL);
|
||||
check_int(err->code, ==, 2);
|
||||
check_str(err->message, ==, "oops");
|
||||
|
||||
tr_error_prefix(&err, "error: ");
|
||||
check_ptr(err, !=, NULL);
|
||||
check_int(err->code, ==, 2);
|
||||
check_str(err->message, ==, "error: oops");
|
||||
|
||||
tr_error_free(err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_error_propagate(void)
|
||||
{
|
||||
tr_error* err = NULL;
|
||||
tr_error* err2 = NULL;
|
||||
|
||||
tr_error_set_literal(&err, 1, "oops");
|
||||
check_ptr(err, !=, NULL);
|
||||
check_int(err->code, ==, 1);
|
||||
check_str(err->message, ==, "oops");
|
||||
|
||||
tr_error_propagate(&err2, &err);
|
||||
check_ptr(err2, !=, NULL);
|
||||
check_int(err2->code, ==, 1);
|
||||
check_str(err2->message, ==, "oops");
|
||||
check_ptr(err, ==, NULL);
|
||||
|
||||
tr_error_propagate_prefixed(&err, &err2, "error: ");
|
||||
check_ptr(err, !=, NULL);
|
||||
check_int(err->code, ==, 1);
|
||||
check_str(err->message, ==, "error: oops");
|
||||
check_ptr(err2, ==, NULL);
|
||||
|
||||
tr_error_propagate(NULL, &err);
|
||||
check_ptr(err, ==, NULL);
|
||||
|
||||
tr_error_free(err2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testFunc const tests[] =
|
||||
{
|
||||
test_error_set,
|
||||
test_error_propagate
|
||||
};
|
||||
|
||||
return runTests(tests, NUM_TESTS(tests));
|
||||
}
|
|
@ -12,10 +12,7 @@
|
|||
|
||||
#include "tr-macros.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* @addtogroup error Error reporting
|
||||
|
@ -134,6 +131,4 @@ void tr_error_propagate_prefixed(tr_error** new_error, tr_error** old_error, cha
|
|||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/sh
|
||||
err=0
|
||||
count=0
|
||||
while [ $err -eq 0 ]; do
|
||||
count=$((count + 1))
|
||||
echo starting run number $count
|
||||
make check
|
||||
err=$?
|
||||
done
|
|
@ -844,6 +844,8 @@ skip_darwin_fcntl:
|
|||
|
||||
bool tr_sys_file_preallocate(tr_sys_file_t handle, uint64_t size, int flags, tr_error** error)
|
||||
{
|
||||
(void)size;
|
||||
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
|
||||
bool ret = false;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -15,10 +15,9 @@
|
|||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
struct tr_error;
|
||||
|
||||
|
@ -664,6 +663,4 @@ bool tr_sys_dir_close(tr_sys_dir_t handle, struct tr_error** error);
|
|||
/** @} */
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h> /* memset() */
|
||||
|
||||
#include "transmission.h"
|
||||
#include "history.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
static int test1(void)
|
||||
{
|
||||
tr_recentHistory h;
|
||||
|
||||
memset(&h, 0, sizeof(tr_recentHistory));
|
||||
|
||||
tr_historyAdd(&h, 10000, 1);
|
||||
check_int((int)tr_historyGet(&h, 12000, 1000), ==, 0);
|
||||
check_int((int)tr_historyGet(&h, 12000, 3000), ==, 1);
|
||||
check_int((int)tr_historyGet(&h, 12000, 5000), ==, 1);
|
||||
tr_historyAdd(&h, 20000, 1);
|
||||
check_int((int)tr_historyGet(&h, 22000, 1000), ==, 0);
|
||||
check_int((int)tr_historyGet(&h, 22000, 3000), ==, 1);
|
||||
check_int((int)tr_historyGet(&h, 22000, 15000), ==, 2);
|
||||
check_int((int)tr_historyGet(&h, 22000, 20000), ==, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MAIN_SINGLE_TEST(test1)
|
|
@ -12,6 +12,12 @@
|
|||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
#include <time.h> /* time_t */
|
||||
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* A generic short-term memory object that remembers how many times
|
||||
* something happened over the last N seconds.
|
||||
|
@ -54,3 +60,5 @@ void tr_historyAdd(tr_recentHistory*, time_t when, unsigned int n);
|
|||
* @param seconds how many seconds to count back through.
|
||||
*/
|
||||
unsigned int tr_historyGet(tr_recentHistory const*, time_t when, unsigned int seconds);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,286 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h> /* strlen() */
|
||||
|
||||
#include <locale.h> /* setlocale() */
|
||||
|
||||
#define __LIBTRANSMISSION_VARIANT_MODULE__
|
||||
|
||||
#include "transmission.h"
|
||||
#include "utils.h" /* tr_free */
|
||||
#include "variant.h"
|
||||
#include "variant-common.h"
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
static int test_elements(void)
|
||||
{
|
||||
char const* in;
|
||||
tr_variant top;
|
||||
char const* str;
|
||||
bool f;
|
||||
double d;
|
||||
int64_t i;
|
||||
int err = 0;
|
||||
tr_quark key;
|
||||
|
||||
in =
|
||||
"{ \"string\": \"hello world\","
|
||||
" \"escaped\": \"bell \\b formfeed \\f linefeed \\n carriage return \\r tab \\t\","
|
||||
" \"int\": 5, "
|
||||
" \"float\": 6.5, "
|
||||
" \"true\": true, "
|
||||
" \"false\": false, "
|
||||
" \"null\": null }";
|
||||
|
||||
err = tr_variantFromJson(&top, in, strlen(in));
|
||||
check_int(err, ==, 0);
|
||||
check(tr_variantIsDict(&top));
|
||||
str = NULL;
|
||||
key = tr_quark_new("string", 6);
|
||||
check(tr_variantDictFindStr(&top, key, &str, NULL));
|
||||
check_str(str, ==, "hello world");
|
||||
check(tr_variantDictFindStr(&top, tr_quark_new("escaped", 7), &str, NULL));
|
||||
check_str(str, ==, "bell \b formfeed \f linefeed \n carriage return \r tab \t");
|
||||
i = 0;
|
||||
check(tr_variantDictFindInt(&top, tr_quark_new("int", 3), &i));
|
||||
check_int(i, ==, 5);
|
||||
d = 0;
|
||||
check(tr_variantDictFindReal(&top, tr_quark_new("float", 5), &d));
|
||||
check_int(((int)(d * 10)), ==, 65);
|
||||
f = false;
|
||||
check(tr_variantDictFindBool(&top, tr_quark_new("true", 4), &f));
|
||||
check_int(f, ==, true);
|
||||
check(tr_variantDictFindBool(&top, tr_quark_new("false", 5), &f));
|
||||
check_int(f, ==, false);
|
||||
check(tr_variantDictFindStr(&top, tr_quark_new("null", 4), &str, NULL));
|
||||
check_str(str, ==, "");
|
||||
|
||||
if (err == 0)
|
||||
{
|
||||
tr_variantFree(&top);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_utf8(void)
|
||||
{
|
||||
char const* in = "{ \"key\": \"Letöltések\" }";
|
||||
tr_variant top;
|
||||
char const* str;
|
||||
char* json;
|
||||
int err;
|
||||
tr_quark const key = tr_quark_new("key", 3);
|
||||
|
||||
err = tr_variantFromJson(&top, in, strlen(in));
|
||||
check_int(err, ==, 0);
|
||||
check(tr_variantIsDict(&top));
|
||||
check(tr_variantDictFindStr(&top, key, &str, NULL));
|
||||
check_str(str, ==, "Letöltések");
|
||||
|
||||
if (err == 0)
|
||||
{
|
||||
tr_variantFree(&top);
|
||||
}
|
||||
|
||||
in = "{ \"key\": \"\\u005C\" }";
|
||||
err = tr_variantFromJson(&top, in, strlen(in));
|
||||
check_int(err, ==, 0);
|
||||
check(tr_variantIsDict(&top));
|
||||
check(tr_variantDictFindStr(&top, key, &str, NULL));
|
||||
check_str(str, ==, "\\");
|
||||
|
||||
if (err == 0)
|
||||
{
|
||||
tr_variantFree(&top);
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Feed it JSON-escaped nonascii to the JSON decoder.
|
||||
* 2. Confirm that the result is UTF-8.
|
||||
* 3. Feed the same UTF-8 back into the JSON encoder.
|
||||
* 4. Confirm that the result is JSON-escaped.
|
||||
* 5. Dogfood that result back into the parser.
|
||||
* 6. Confirm that the result is UTF-8.
|
||||
*/
|
||||
in = "{ \"key\": \"Let\\u00f6lt\\u00e9sek\" }";
|
||||
err = tr_variantFromJson(&top, in, strlen(in));
|
||||
check_int(err, ==, 0);
|
||||
check(tr_variantIsDict(&top));
|
||||
check(tr_variantDictFindStr(&top, key, &str, NULL));
|
||||
check_str(str, ==, "Letöltések");
|
||||
json = tr_variantToStr(&top, TR_VARIANT_FMT_JSON, NULL);
|
||||
|
||||
if (err == 0)
|
||||
{
|
||||
tr_variantFree(&top);
|
||||
}
|
||||
|
||||
check_ptr(json, !=, NULL);
|
||||
check_str(strstr(json, "\\u00f6"), !=, NULL);
|
||||
check_str(strstr(json, "\\u00e9"), !=, NULL);
|
||||
err = tr_variantFromJson(&top, json, strlen(json));
|
||||
check_int(err, ==, 0);
|
||||
check(tr_variantIsDict(&top));
|
||||
check(tr_variantDictFindStr(&top, key, &str, NULL));
|
||||
check_str(str, ==, "Letöltések");
|
||||
|
||||
if (err == 0)
|
||||
{
|
||||
tr_variantFree(&top);
|
||||
}
|
||||
|
||||
tr_free(json);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test1(void)
|
||||
{
|
||||
char const* in =
|
||||
"{\n"
|
||||
" \"headers\": {\n"
|
||||
" \"type\": \"request\",\n"
|
||||
" \"tag\": 666\n"
|
||||
" },\n"
|
||||
" \"body\": {\n"
|
||||
" \"name\": \"torrent-info\",\n"
|
||||
" \"arguments\": {\n"
|
||||
" \"ids\": [ 7, 10 ]\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
tr_variant top;
|
||||
tr_variant* headers;
|
||||
tr_variant* body;
|
||||
tr_variant* args;
|
||||
tr_variant* ids;
|
||||
char const* str;
|
||||
int64_t i;
|
||||
int const err = tr_variantFromJson(&top, in, strlen(in));
|
||||
|
||||
check_int(err, ==, 0);
|
||||
check(tr_variantIsDict(&top));
|
||||
check_ptr((headers = tr_variantDictFind(&top, tr_quark_new("headers", 7))), !=, NULL);
|
||||
check(tr_variantIsDict(headers));
|
||||
check(tr_variantDictFindStr(headers, tr_quark_new("type", 4), &str, NULL));
|
||||
check_str(str, ==, "request");
|
||||
check(tr_variantDictFindInt(headers, TR_KEY_tag, &i));
|
||||
check_int(i, ==, 666);
|
||||
check_ptr((body = tr_variantDictFind(&top, tr_quark_new("body", 4))), !=, NULL);
|
||||
check(tr_variantDictFindStr(body, TR_KEY_name, &str, NULL));
|
||||
check_str(str, ==, "torrent-info");
|
||||
check_ptr((args = tr_variantDictFind(body, tr_quark_new("arguments", 9))), !=, NULL);
|
||||
check(tr_variantIsDict(args));
|
||||
check_ptr((ids = tr_variantDictFind(args, TR_KEY_ids)), !=, NULL);
|
||||
check(tr_variantIsList(ids));
|
||||
check_uint(tr_variantListSize(ids), ==, 2);
|
||||
check(tr_variantGetInt(tr_variantListChild(ids, 0), &i));
|
||||
check_int(i, ==, 7);
|
||||
check(tr_variantGetInt(tr_variantListChild(ids, 1), &i));
|
||||
check_int(i, ==, 10);
|
||||
|
||||
tr_variantFree(&top);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test2(void)
|
||||
{
|
||||
tr_variant top;
|
||||
char const* in = " ";
|
||||
int err;
|
||||
|
||||
top.type = 0;
|
||||
err = tr_variantFromJson(&top, in, strlen(in));
|
||||
|
||||
check_int(err, !=, 0);
|
||||
check(!tr_variantIsDict(&top));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test3(void)
|
||||
{
|
||||
char const* in =
|
||||
"{ \"error\": 2,"
|
||||
" \"errorString\": \"torrent not registered with this tracker 6UHsVW'*C\","
|
||||
" \"eta\": 262792,"
|
||||
" \"id\": 25,"
|
||||
" \"leftUntilDone\": 2275655680 }";
|
||||
tr_variant top;
|
||||
char const* str;
|
||||
|
||||
int const err = tr_variantFromJson(&top, in, strlen(in));
|
||||
check_int(err, ==, 0);
|
||||
check(tr_variantDictFindStr(&top, TR_KEY_errorString, &str, NULL));
|
||||
check_str(str, ==, "torrent not registered with this tracker 6UHsVW'*C");
|
||||
|
||||
tr_variantFree(&top);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_unescape(void)
|
||||
{
|
||||
char const* in = "{ \"string-1\": \"\\/usr\\/lib\" }";
|
||||
tr_variant top;
|
||||
char const* str;
|
||||
|
||||
int const err = tr_variantFromJson(&top, in, strlen(in));
|
||||
check_int(err, ==, 0);
|
||||
check(tr_variantDictFindStr(&top, tr_quark_new("string-1", 8), &str, NULL));
|
||||
check_str(str, ==, "/usr/lib");
|
||||
|
||||
tr_variantFree(&top);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char const* comma_locales[] =
|
||||
{
|
||||
"da_DK.UTF-8",
|
||||
"fr_FR.UTF-8",
|
||||
"ru_RU.UTF-8"
|
||||
};
|
||||
|
||||
testFunc const tests[] =
|
||||
{
|
||||
test_elements,
|
||||
test_utf8,
|
||||
test1,
|
||||
test2,
|
||||
test3,
|
||||
test_unescape
|
||||
};
|
||||
|
||||
/* run the tests in a locale with a decimal point of '.' */
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
|
||||
int ret = runTests(tests, NUM_TESTS(tests));
|
||||
|
||||
/* run the tests in a locale with a decimal point of ',' */
|
||||
bool is_locale_set = false;
|
||||
|
||||
for (size_t i = 0; !is_locale_set && i < TR_N_ELEMENTS(comma_locales); ++i)
|
||||
{
|
||||
is_locale_set = setlocale(LC_NUMERIC, comma_locales[i]) != NULL;
|
||||
}
|
||||
|
||||
if (!is_locale_set)
|
||||
{
|
||||
fprintf(stderr, "WARNING: unable to run locale-specific json tests. add a locale like %s or %s\n", comma_locales[0],
|
||||
comma_locales[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret += runTests(tests, NUM_TESTS(tests));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,589 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2017 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> /* mkstemp() */
|
||||
#include <string.h> /* strcmp() */
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h> /* sync() */
|
||||
#endif
|
||||
|
||||
#include "transmission.h"
|
||||
#include "crypto-utils.h"
|
||||
#include "error.h"
|
||||
#include "file.h"
|
||||
#include "platform.h" /* TR_PATH_DELIMETER */
|
||||
#include "torrent.h"
|
||||
#include "tr-assert.h"
|
||||
#include "trevent.h"
|
||||
#include "variant.h"
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
bool verbose = false;
|
||||
|
||||
int current_test = 0;
|
||||
|
||||
bool should_print(bool pass)
|
||||
{
|
||||
if (!pass)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
#ifdef VERBOSE
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool libtest_check(char const* file, int line, bool pass, bool condition, char const* condition_str)
|
||||
{
|
||||
if (should_print(pass))
|
||||
{
|
||||
fprintf(stderr, "%s %s:%d: %s (%s)\n", pass ? "PASS" : "FAIL", file, line, condition_str, condition ? "true" : "false");
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
bool libtest_check_bool(char const* file, int line, bool pass, bool lhs, bool rhs, char const* lhs_str, char const* op_str,
|
||||
char const* rhs_str)
|
||||
{
|
||||
if (should_print(pass))
|
||||
{
|
||||
fprintf(stderr, "%s %s:%d: %s %s %s (%s %s %s)\n", pass ? "PASS" : "FAIL", file, line, lhs_str, op_str, rhs_str,
|
||||
lhs ? "true" : "false", op_str, rhs ? "true" : "false");
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
bool libtest_check_str(char const* file, int line, bool pass, char const* lhs, char const* rhs, char const* lhs_str,
|
||||
char const* op_str, char const* rhs_str)
|
||||
{
|
||||
if (should_print(pass))
|
||||
{
|
||||
char const* const lhs_quote = lhs != NULL ? "\"" : "";
|
||||
char const* const rhs_quote = rhs != NULL ? "\"" : "";
|
||||
|
||||
fprintf(stderr, "%s %s:%d: %s %s %s (%s%s%s %s %s%s%s)\n", pass ? "PASS" : "FAIL", file, line, lhs_str, op_str, rhs_str,
|
||||
lhs_quote, lhs != NULL ? lhs : "NULL", lhs_quote, op_str, rhs_quote, rhs != NULL ? rhs : "NULL", rhs_quote);
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
static void print_mem(FILE* stream, void const* data, size_t size)
|
||||
{
|
||||
if (data == NULL)
|
||||
{
|
||||
fprintf(stream, "NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
fprintf(stream, "(no bytes)");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t const* byte_data = data;
|
||||
|
||||
fprintf(stream, "x'");
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
fprintf(stream, "%02x", (unsigned int)byte_data[i]);
|
||||
}
|
||||
|
||||
fprintf(stream, "'");
|
||||
}
|
||||
|
||||
bool libtest_check_mem(char const* file, int line, bool pass, void const* lhs, void const* rhs, size_t size,
|
||||
char const* lhs_str, char const* op_str, char const* rhs_str)
|
||||
{
|
||||
if (should_print(pass))
|
||||
{
|
||||
fprintf(stderr, "%s %s:%d: %s %s %s (", pass ? "PASS" : "FAIL", file, line, lhs_str, op_str, rhs_str);
|
||||
print_mem(stderr, lhs, size);
|
||||
fprintf(stderr, " %s ", op_str);
|
||||
print_mem(stderr, rhs, size);
|
||||
fprintf(stderr, ")\n");
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
bool libtest_check_int(char const* file, int line, bool pass, intmax_t lhs, intmax_t rhs, char const* lhs_str,
|
||||
char const* op_str, char const* rhs_str)
|
||||
{
|
||||
if (should_print(pass))
|
||||
{
|
||||
fprintf(stderr, "%s %s:%d: %s %s %s (%jd %s %jd)\n", pass ? "PASS" : "FAIL", file, line, lhs_str, op_str, rhs_str, lhs,
|
||||
op_str, rhs);
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
bool libtest_check_uint(char const* file, int line, bool pass, uintmax_t lhs, uintmax_t rhs, char const* lhs_str,
|
||||
char const* op_str, char const* rhs_str)
|
||||
{
|
||||
if (should_print(pass))
|
||||
{
|
||||
fprintf(stderr, "%s %s:%d: %s %s %s (%ju %s %ju)\n", pass ? "PASS" : "FAIL", file, line, lhs_str, op_str, rhs_str, lhs,
|
||||
op_str, rhs);
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
bool libtest_check_ptr(char const* file, int line, bool pass, void const* lhs, void const* rhs, char const* lhs_str,
|
||||
char const* op_str, char const* rhs_str)
|
||||
{
|
||||
if (should_print(pass))
|
||||
{
|
||||
fprintf(stderr, "%s %s:%d: %s %s %s (%p %s %p)\n", pass ? "PASS" : "FAIL", file, line, lhs_str, op_str, rhs_str, lhs,
|
||||
op_str, rhs);
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
int runTests(testFunc const* const tests, int numTests)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
(void)current_test; /* Use test even if we don't have any tests to run */
|
||||
|
||||
for (int i = 0; i < numTests; i++)
|
||||
{
|
||||
if ((*tests[i])() != 0)
|
||||
{
|
||||
++ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static char* tr_getcwd(void)
|
||||
{
|
||||
char* result;
|
||||
tr_error* error = NULL;
|
||||
|
||||
result = tr_sys_dir_get_current(&error);
|
||||
|
||||
if (result == NULL)
|
||||
{
|
||||
fprintf(stderr, "getcwd error: \"%s\"", error->message);
|
||||
tr_error_free(error);
|
||||
result = tr_strdup("");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
char* libtest_sandbox_create(void)
|
||||
{
|
||||
char* path = tr_getcwd();
|
||||
char* sandbox = tr_buildPath(path, "sandbox-XXXXXX", NULL);
|
||||
tr_free(path);
|
||||
tr_sys_dir_create_temp(sandbox, NULL);
|
||||
return tr_sys_path_native_separators(sandbox);
|
||||
}
|
||||
|
||||
static void rm_rf(char const* killme)
|
||||
{
|
||||
tr_sys_path_info info;
|
||||
|
||||
if (!tr_sys_path_get_info(killme, 0, &info, NULL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tr_sys_dir_t odir = info.type == TR_SYS_PATH_IS_DIRECTORY ? tr_sys_dir_open(killme, NULL) : TR_BAD_SYS_DIR;
|
||||
|
||||
if (odir != TR_BAD_SYS_DIR)
|
||||
{
|
||||
char const* name;
|
||||
|
||||
while ((name = tr_sys_dir_read_name(odir, NULL)) != NULL)
|
||||
{
|
||||
if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0)
|
||||
{
|
||||
char* tmp = tr_buildPath(killme, name, NULL);
|
||||
rm_rf(tmp);
|
||||
tr_free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
tr_sys_dir_close(odir, NULL);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
fprintf(stderr, "cleanup: removing %s\n", killme);
|
||||
}
|
||||
|
||||
tr_sys_path_remove(killme, NULL);
|
||||
}
|
||||
|
||||
void libtest_sandbox_destroy(char const* sandbox)
|
||||
{
|
||||
rm_rf(sandbox);
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
#define MEM_K 1024
|
||||
#define MEM_K_STR "KiB"
|
||||
#define MEM_M_STR "MiB"
|
||||
#define MEM_G_STR "GiB"
|
||||
#define MEM_T_STR "TiB"
|
||||
|
||||
#define DISK_K 1000
|
||||
#define DISK_K_STR "kB"
|
||||
#define DISK_M_STR "MB"
|
||||
#define DISK_G_STR "GB"
|
||||
#define DISK_T_STR "TB"
|
||||
|
||||
#define SPEED_K 1000
|
||||
#define SPEED_K_STR "kB/s"
|
||||
#define SPEED_M_STR "MB/s"
|
||||
#define SPEED_G_STR "GB/s"
|
||||
#define SPEED_T_STR "TB/s"
|
||||
|
||||
tr_session* libttest_session_init(tr_variant* settings)
|
||||
{
|
||||
size_t len;
|
||||
char const* str;
|
||||
char* sandbox;
|
||||
char* path;
|
||||
tr_quark q;
|
||||
static bool formatters_inited = false;
|
||||
tr_session* session;
|
||||
tr_variant local_settings;
|
||||
|
||||
tr_variantInitDict(&local_settings, 10);
|
||||
|
||||
if (settings == NULL)
|
||||
{
|
||||
settings = &local_settings;
|
||||
}
|
||||
|
||||
sandbox = libtest_sandbox_create();
|
||||
|
||||
if (!formatters_inited)
|
||||
{
|
||||
formatters_inited = true;
|
||||
tr_formatter_mem_init(MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR);
|
||||
tr_formatter_size_init(DISK_K, DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR);
|
||||
tr_formatter_speed_init(SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR);
|
||||
}
|
||||
|
||||
/* download dir */
|
||||
q = TR_KEY_download_dir;
|
||||
|
||||
if (tr_variantDictFindStr(settings, q, &str, &len))
|
||||
{
|
||||
path = tr_strdup_printf("%s/%*.*s", sandbox, (int)len, (int)len, str);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = tr_buildPath(sandbox, "Downloads", NULL);
|
||||
}
|
||||
|
||||
tr_sys_dir_create(path, TR_SYS_DIR_CREATE_PARENTS, 0700, NULL);
|
||||
tr_variantDictAddStr(settings, q, path);
|
||||
tr_free(path);
|
||||
|
||||
/* incomplete dir */
|
||||
q = TR_KEY_incomplete_dir;
|
||||
|
||||
if (tr_variantDictFindStr(settings, q, &str, &len))
|
||||
{
|
||||
path = tr_strdup_printf("%s/%*.*s", sandbox, (int)len, (int)len, str);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = tr_buildPath(sandbox, "Incomplete", NULL);
|
||||
}
|
||||
|
||||
tr_variantDictAddStr(settings, q, path);
|
||||
tr_free(path);
|
||||
|
||||
path = tr_buildPath(sandbox, "blocklists", NULL);
|
||||
tr_sys_dir_create(path, TR_SYS_DIR_CREATE_PARENTS, 0700, NULL);
|
||||
tr_free(path);
|
||||
|
||||
q = TR_KEY_port_forwarding_enabled;
|
||||
|
||||
if (tr_variantDictFind(settings, q) == NULL)
|
||||
{
|
||||
tr_variantDictAddBool(settings, q, false);
|
||||
}
|
||||
|
||||
q = TR_KEY_dht_enabled;
|
||||
|
||||
if (tr_variantDictFind(settings, q) == NULL)
|
||||
{
|
||||
tr_variantDictAddBool(settings, q, false);
|
||||
}
|
||||
|
||||
q = TR_KEY_message_level;
|
||||
|
||||
if (tr_variantDictFind(settings, q) == NULL)
|
||||
{
|
||||
tr_variantDictAddInt(settings, q, verbose ? TR_LOG_DEBUG : TR_LOG_ERROR);
|
||||
}
|
||||
|
||||
session = tr_sessionInit(sandbox, !verbose, settings);
|
||||
|
||||
tr_free(sandbox);
|
||||
tr_variantFree(&local_settings);
|
||||
return session;
|
||||
}
|
||||
|
||||
void libttest_session_close(tr_session* session)
|
||||
{
|
||||
char* sandbox;
|
||||
|
||||
sandbox = tr_strdup(tr_sessionGetConfigDir(session));
|
||||
tr_sessionClose(session);
|
||||
tr_logFreeQueue(tr_logGetQueue());
|
||||
session = NULL;
|
||||
|
||||
libtest_sandbox_destroy(sandbox);
|
||||
tr_free(sandbox);
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
tr_torrent* libttest_zero_torrent_init(tr_session* session)
|
||||
{
|
||||
int err;
|
||||
size_t metainfo_len;
|
||||
char* metainfo;
|
||||
char const* metainfo_base64;
|
||||
tr_torrent* tor;
|
||||
tr_ctor* ctor;
|
||||
|
||||
/*
|
||||
1048576 files-filled-with-zeroes/1048576
|
||||
4096 files-filled-with-zeroes/4096
|
||||
512 files-filled-with-zeroes/512
|
||||
*/
|
||||
metainfo_base64 =
|
||||
"ZDg6YW5ub3VuY2UzMTpodHRwOi8vd3d3LmV4YW1wbGUuY29tL2Fubm91bmNlMTA6Y3JlYXRlZCBi"
|
||||
"eTI1OlRyYW5zbWlzc2lvbi8yLjYxICgxMzQwNykxMzpjcmVhdGlvbiBkYXRlaTEzNTg3MDQwNzVl"
|
||||
"ODplbmNvZGluZzU6VVRGLTg0OmluZm9kNTpmaWxlc2xkNjpsZW5ndGhpMTA0ODU3NmU0OnBhdGhs"
|
||||
"NzoxMDQ4NTc2ZWVkNjpsZW5ndGhpNDA5NmU0OnBhdGhsNDo0MDk2ZWVkNjpsZW5ndGhpNTEyZTQ6"
|
||||
"cGF0aGwzOjUxMmVlZTQ6bmFtZTI0OmZpbGVzLWZpbGxlZC13aXRoLXplcm9lczEyOnBpZWNlIGxl"
|
||||
"bmd0aGkzMjc2OGU2OnBpZWNlczY2MDpRiEMYSbRhMVL9e9umo/8KT9ZCS1GIQxhJtGExUv1726aj"
|
||||
"/wpP1kJLUYhDGEm0YTFS/XvbpqP/Ck/WQktRiEMYSbRhMVL9e9umo/8KT9ZCS1GIQxhJtGExUv17"
|
||||
"26aj/wpP1kJLUYhDGEm0YTFS/XvbpqP/Ck/WQktRiEMYSbRhMVL9e9umo/8KT9ZCS1GIQxhJtGEx"
|
||||
"Uv1726aj/wpP1kJLUYhDGEm0YTFS/XvbpqP/Ck/WQktRiEMYSbRhMVL9e9umo/8KT9ZCS1GIQxhJ"
|
||||
"tGExUv1726aj/wpP1kJLUYhDGEm0YTFS/XvbpqP/Ck/WQktRiEMYSbRhMVL9e9umo/8KT9ZCS1GI"
|
||||
"QxhJtGExUv1726aj/wpP1kJLUYhDGEm0YTFS/XvbpqP/Ck/WQktRiEMYSbRhMVL9e9umo/8KT9ZC"
|
||||
"S1GIQxhJtGExUv1726aj/wpP1kJLUYhDGEm0YTFS/XvbpqP/Ck/WQktRiEMYSbRhMVL9e9umo/8K"
|
||||
"T9ZCS1GIQxhJtGExUv1726aj/wpP1kJLUYhDGEm0YTFS/XvbpqP/Ck/WQktRiEMYSbRhMVL9e9um"
|
||||
"o/8KT9ZCS1GIQxhJtGExUv1726aj/wpP1kJLUYhDGEm0YTFS/XvbpqP/Ck/WQktRiEMYSbRhMVL9"
|
||||
"e9umo/8KT9ZCS1GIQxhJtGExUv1726aj/wpP1kJLUYhDGEm0YTFS/XvbpqP/Ck/WQktRiEMYSbRh"
|
||||
"MVL9e9umo/8KT9ZCS1GIQxhJtGExUv1726aj/wpP1kJLUYhDGEm0YTFS/XvbpqP/Ck/WQktRiEMY"
|
||||
"SbRhMVL9e9umo/8KT9ZCS1GIQxhJtGExUv1726aj/wpP1kJLOlf5A+Tz30nMBVuNM2hpV3wg/103"
|
||||
"OnByaXZhdGVpMGVlZQ==";
|
||||
|
||||
/* create the torrent ctor */
|
||||
metainfo = tr_base64_decode_str(metainfo_base64, &metainfo_len);
|
||||
TR_ASSERT(metainfo != NULL);
|
||||
TR_ASSERT(metainfo_len > 0);
|
||||
TR_ASSERT(session != NULL);
|
||||
ctor = tr_ctorNew(session);
|
||||
tr_ctorSetMetainfo(ctor, (uint8_t*)metainfo, metainfo_len);
|
||||
tr_ctorSetPaused(ctor, TR_FORCE, true);
|
||||
|
||||
/* create the torrent */
|
||||
err = 0;
|
||||
tor = tr_torrentNew(ctor, &err, NULL);
|
||||
TR_ASSERT(err == 0);
|
||||
|
||||
/* cleanup */
|
||||
tr_free(metainfo);
|
||||
tr_ctorFree(ctor);
|
||||
return tor;
|
||||
}
|
||||
|
||||
void libttest_zero_torrent_populate(tr_torrent* tor, bool complete)
|
||||
{
|
||||
for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
|
||||
{
|
||||
int err;
|
||||
tr_sys_file_t fd;
|
||||
char* path;
|
||||
char* dirname;
|
||||
tr_file const* file = &tor->info.files[i];
|
||||
|
||||
if (!complete && i == 0)
|
||||
{
|
||||
path = tr_strdup_printf("%s%c%s.part", tor->currentDir, TR_PATH_DELIMITER, file->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = tr_strdup_printf("%s%c%s", tor->currentDir, TR_PATH_DELIMITER, file->name);
|
||||
}
|
||||
|
||||
dirname = tr_sys_path_dirname(path, NULL);
|
||||
tr_sys_dir_create(dirname, TR_SYS_DIR_CREATE_PARENTS, 0700, NULL);
|
||||
fd = tr_sys_file_open(path, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_TRUNCATE, 0600, NULL);
|
||||
|
||||
for (uint64_t j = 0; j < file->length; ++j)
|
||||
{
|
||||
tr_sys_file_write(fd, (!complete && i == 0 && j < tor->info.pieceSize) ? "\1" : "\0", 1, NULL, NULL);
|
||||
}
|
||||
|
||||
tr_sys_file_close(fd, NULL);
|
||||
|
||||
tr_free(dirname);
|
||||
tr_free(path);
|
||||
|
||||
path = tr_torrentFindFile(tor, i);
|
||||
TR_ASSERT(path != NULL);
|
||||
err = errno;
|
||||
TR_ASSERT(tr_sys_path_exists(path, NULL));
|
||||
errno = err;
|
||||
tr_free(path);
|
||||
}
|
||||
|
||||
libttest_sync();
|
||||
libttest_blockingTorrentVerify(tor);
|
||||
|
||||
if (complete)
|
||||
{
|
||||
TR_ASSERT(tr_torrentStat(tor)->leftUntilDone == 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
TR_ASSERT(tr_torrentStat(tor)->leftUntilDone == tor->info.pieceSize);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static void onVerifyDone(tr_torrent* tor UNUSED, bool aborted UNUSED, void* done)
|
||||
{
|
||||
*(bool*)done = true;
|
||||
}
|
||||
|
||||
void libttest_blockingTorrentVerify(tr_torrent* tor)
|
||||
{
|
||||
TR_ASSERT(tor->session != NULL);
|
||||
TR_ASSERT(!tr_amInEventThread(tor->session));
|
||||
|
||||
bool done = false;
|
||||
|
||||
tr_torrentVerify(tor, onVerifyDone, &done);
|
||||
|
||||
while (!done)
|
||||
{
|
||||
tr_wait_msec(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void build_parent_dir(char const* path)
|
||||
{
|
||||
char* dir;
|
||||
tr_error* error = NULL;
|
||||
int const tmperr = errno;
|
||||
|
||||
dir = tr_sys_path_dirname(path, NULL);
|
||||
tr_sys_dir_create(dir, TR_SYS_DIR_CREATE_PARENTS, 0700, &error);
|
||||
TR_ASSERT(error == NULL);
|
||||
tr_free(dir);
|
||||
|
||||
errno = tmperr;
|
||||
}
|
||||
|
||||
void libtest_create_file_with_contents(char const* path, void const* payload, size_t n)
|
||||
{
|
||||
tr_sys_file_t fd;
|
||||
int const tmperr = errno;
|
||||
|
||||
build_parent_dir(path);
|
||||
|
||||
fd = tr_sys_file_open(path, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_TRUNCATE, 0600, NULL);
|
||||
tr_sys_file_write(fd, payload, n, NULL, NULL);
|
||||
tr_sys_file_close(fd, NULL);
|
||||
|
||||
libttest_sync();
|
||||
|
||||
errno = tmperr;
|
||||
}
|
||||
|
||||
void libtest_create_file_with_string_contents(char const* path, char const* str)
|
||||
{
|
||||
libtest_create_file_with_contents(path, str, strlen(str));
|
||||
}
|
||||
|
||||
void libtest_create_tmpfile_with_contents(char* tmpl, void const* payload, size_t n)
|
||||
{
|
||||
tr_sys_file_t fd;
|
||||
int const tmperr = errno;
|
||||
uint64_t n_left = n;
|
||||
tr_error* error = NULL;
|
||||
|
||||
build_parent_dir(tmpl);
|
||||
|
||||
fd = tr_sys_file_open_temp(tmpl, NULL);
|
||||
|
||||
while (n_left > 0)
|
||||
{
|
||||
uint64_t n;
|
||||
|
||||
if (!tr_sys_file_write(fd, payload, n_left, &n, &error))
|
||||
{
|
||||
fprintf(stderr, "Error writing '%s': %s\n", tmpl, error->message);
|
||||
tr_error_free(error);
|
||||
break;
|
||||
}
|
||||
|
||||
n_left -= n;
|
||||
}
|
||||
|
||||
tr_sys_file_close(fd, NULL);
|
||||
|
||||
libttest_sync();
|
||||
|
||||
errno = tmperr;
|
||||
}
|
||||
|
||||
void libttest_sync(void)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
sync();
|
||||
#endif
|
||||
}
|
|
@ -1,192 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2010-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Note VERBOSE needs to be (un)defined before including this file */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h> /* strlen() */
|
||||
|
||||
#include "transmission.h"
|
||||
#include "utils.h" /* tr_strcmp0() */
|
||||
|
||||
extern int current_test;
|
||||
|
||||
extern bool verbose;
|
||||
|
||||
bool should_print(bool pass);
|
||||
|
||||
bool libtest_check(char const* file, int line, bool pass, bool condition, char const* condition_str);
|
||||
bool libtest_check_bool(char const* file, int line, bool pass, bool lhs, bool rhs, char const* lhs_str, char const* op_str,
|
||||
char const* rhs_str);
|
||||
bool libtest_check_str(char const* file, int line, bool pass, char const* lhs, char const* rhs, char const* lhs_str,
|
||||
char const* op_str, char const* rhs_str);
|
||||
bool libtest_check_mem(char const* file, int line, bool pass, void const* lhs, void const* rhs, size_t size,
|
||||
char const* lhs_str, char const* op_str, char const* rhs_str);
|
||||
bool libtest_check_int(char const* file, int line, bool pass, intmax_t lhs, intmax_t rhs, char const* lhs_str,
|
||||
char const* op_str, char const* rhs_str);
|
||||
bool libtest_check_uint(char const* file, int line, bool pass, uintmax_t lhs, uintmax_t rhs, char const* lhs_str,
|
||||
char const* op_str, char const* rhs_str);
|
||||
bool libtest_check_ptr(char const* file, int line, bool pass, void const* lhs, void const* rhs, char const* lhs_str,
|
||||
char const* op_str, char const* rhs_str);
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
#define check(condition) \
|
||||
do \
|
||||
{ \
|
||||
++current_test; \
|
||||
\
|
||||
bool const check_result = (condition); \
|
||||
\
|
||||
if (!libtest_check(__FILE__, __LINE__, check_result, check_result, #condition)) \
|
||||
{ \
|
||||
return current_test; \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define check_bool(lhs, op, rhs) \
|
||||
do \
|
||||
{ \
|
||||
++current_test; \
|
||||
\
|
||||
bool const check_lhs = (lhs); \
|
||||
bool const check_rhs = (rhs); \
|
||||
\
|
||||
bool const check_result = check_lhs op check_rhs; \
|
||||
\
|
||||
if (!libtest_check_bool(__FILE__, __LINE__, check_result, check_lhs, check_rhs, #lhs, #op, #rhs)) \
|
||||
{ \
|
||||
return current_test; \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define check_str(lhs, op, rhs) \
|
||||
do \
|
||||
{ \
|
||||
++current_test; \
|
||||
\
|
||||
char const* const check_lhs = (lhs); \
|
||||
char const* const check_rhs = (rhs); \
|
||||
\
|
||||
bool const check_result = tr_strcmp0(check_lhs, check_rhs) op 0; \
|
||||
\
|
||||
if (!libtest_check_str(__FILE__, __LINE__, check_result, check_lhs, check_rhs, #lhs, #op, #rhs)) \
|
||||
{ \
|
||||
return current_test; \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define check_mem(lhs, op, rhs, size) \
|
||||
do \
|
||||
{ \
|
||||
++current_test; \
|
||||
\
|
||||
void const* const check_lhs = (lhs); \
|
||||
void const* const check_rhs = (rhs); \
|
||||
size_t const check_mem_size = (size); \
|
||||
\
|
||||
bool const check_result = tr_memcmp0(check_lhs, check_rhs, check_mem_size) op 0; \
|
||||
\
|
||||
if (!libtest_check_mem(__FILE__, __LINE__, check_result, check_lhs, check_rhs, check_mem_size, #lhs, #op, #rhs)) \
|
||||
{ \
|
||||
return current_test; \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define check_int(lhs, op, rhs) \
|
||||
do \
|
||||
{ \
|
||||
++current_test; \
|
||||
\
|
||||
intmax_t const check_lhs = (lhs); \
|
||||
intmax_t const check_rhs = (rhs); \
|
||||
\
|
||||
bool const check_result = check_lhs op check_rhs; \
|
||||
\
|
||||
if (!libtest_check_int(__FILE__, __LINE__, check_result, check_lhs, check_rhs, #lhs, #op, #rhs)) \
|
||||
{ \
|
||||
return current_test; \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define check_uint(lhs, op, rhs) \
|
||||
do \
|
||||
{ \
|
||||
++current_test; \
|
||||
\
|
||||
uintmax_t const check_lhs = (lhs); \
|
||||
uintmax_t const check_rhs = (rhs); \
|
||||
\
|
||||
bool const check_result = check_lhs op check_rhs; \
|
||||
\
|
||||
if (!libtest_check_uint(__FILE__, __LINE__, check_result, check_lhs, check_rhs, #lhs, #op, #rhs)) \
|
||||
{ \
|
||||
return current_test; \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define check_ptr(lhs, op, rhs) \
|
||||
do \
|
||||
{ \
|
||||
++current_test; \
|
||||
\
|
||||
void const* const check_lhs = (lhs); \
|
||||
void const* const check_rhs = (rhs); \
|
||||
\
|
||||
bool const check_result = check_lhs op check_rhs; \
|
||||
\
|
||||
if (!libtest_check_ptr(__FILE__, __LINE__, check_result, check_lhs, check_rhs, #lhs, #op, #rhs)) \
|
||||
{ \
|
||||
return current_test; \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
typedef int (* testFunc)(void);
|
||||
|
||||
#define NUM_TESTS(tarray) ((int)(sizeof(tarray) / sizeof(tarray[0])))
|
||||
|
||||
int runTests(testFunc const* const tests, int numTests);
|
||||
|
||||
#define MAIN_SINGLE_TEST(test) \
|
||||
int main(void) \
|
||||
{ \
|
||||
testFunc const tests[] = { test }; \
|
||||
return runTests(tests, 1); \
|
||||
}
|
||||
|
||||
tr_session* libttest_session_init(struct tr_variant* settings);
|
||||
void libttest_session_close(tr_session* session);
|
||||
|
||||
void libttest_zero_torrent_populate(tr_torrent* tor, bool complete);
|
||||
tr_torrent* libttest_zero_torrent_init(tr_session* session);
|
||||
|
||||
void libttest_blockingTorrentVerify(tr_torrent* tor);
|
||||
|
||||
void libtest_create_file_with_contents(char const* path, void const* contents, size_t n);
|
||||
void libtest_create_tmpfile_with_contents(char* tmpl, void const* payload, size_t n);
|
||||
void libtest_create_file_with_string_contents(char const* path, char const* str);
|
||||
|
||||
char* libtest_sandbox_create(void);
|
||||
void libtest_sandbox_destroy(char const* sandbox);
|
||||
|
||||
void libttest_sync(void);
|
|
@ -11,12 +11,10 @@
|
|||
#include <stddef.h> /* size_t */
|
||||
|
||||
#include "file.h" /* tr_sys_file_t */
|
||||
#include "tr-macros.h"
|
||||
#include "utils.h" /* TR_GNUC_PRINTF, TR_GNUC_NONNULL */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
#define TR_LOG_MAX_QUEUE_LENGTH 10000
|
||||
|
||||
|
@ -77,8 +75,6 @@ void tr_logAddDeep(char const* file, int line, char const* name, char const* fmt
|
|||
/** @brief set the buffer with the current time formatted for deep logging. */
|
||||
char* tr_logGetTimeStr(char* buf, size_t buflen) TR_GNUC_NONNULL(1);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2010-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "transmission.h"
|
||||
#include "magnet.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
static int test1(void)
|
||||
{
|
||||
char const* uri;
|
||||
tr_magnet_info* info;
|
||||
uint8_t const dec[] =
|
||||
{
|
||||
210, 53, 64, 16, 163, 202, 74, 222, 91, 116,
|
||||
39, 187, 9, 58, 98, 163, 137, 159, 243, 129
|
||||
};
|
||||
|
||||
uri =
|
||||
"magnet:?xt=urn:btih:"
|
||||
"d2354010a3ca4ade5b7427bb093a62a3899ff381"
|
||||
"&dn=Display%20Name"
|
||||
"&tr=http%3A%2F%2Ftracker.openbittorrent.com%2Fannounce"
|
||||
"&tr=http%3A%2F%2Ftracker.opentracker.org%2Fannounce"
|
||||
"&ws=http%3A%2F%2Fserver.webseed.org%2Fpath%2Fto%2Ffile";
|
||||
info = tr_magnetParse(uri);
|
||||
check_ptr(info, !=, NULL);
|
||||
check_int(info->trackerCount, ==, 2);
|
||||
check_str(info->trackers[0], ==, "http://tracker.openbittorrent.com/announce");
|
||||
check_str(info->trackers[1], ==, "http://tracker.opentracker.org/announce");
|
||||
check_int(info->webseedCount, ==, 1);
|
||||
check_str(info->webseeds[0], ==, "http://server.webseed.org/path/to/file");
|
||||
check_str(info->displayName, ==, "Display Name");
|
||||
check_mem(info->hash, ==, dec, 20);
|
||||
|
||||
tr_magnetFree(info);
|
||||
info = NULL;
|
||||
|
||||
/* same thing but in base32 encoding */
|
||||
uri =
|
||||
"magnet:?xt=urn:btih:"
|
||||
"2I2UAEFDZJFN4W3UE65QSOTCUOEZ744B"
|
||||
"&dn=Display%20Name"
|
||||
"&tr=http%3A%2F%2Ftracker.openbittorrent.com%2Fannounce"
|
||||
"&ws=http%3A%2F%2Fserver.webseed.org%2Fpath%2Fto%2Ffile"
|
||||
"&tr=http%3A%2F%2Ftracker.opentracker.org%2Fannounce";
|
||||
info = tr_magnetParse(uri);
|
||||
check(info != NULL);
|
||||
check_int(info->trackerCount, ==, 2);
|
||||
check_str(info->trackers[0], ==, "http://tracker.openbittorrent.com/announce");
|
||||
check_str(info->trackers[1], ==, "http://tracker.opentracker.org/announce");
|
||||
check_int(info->webseedCount, ==, 1);
|
||||
check_str(info->webseeds[0], ==, "http://server.webseed.org/path/to/file");
|
||||
check_str(info->displayName, ==, "Display Name");
|
||||
check_mem(info->hash, ==, dec, 20);
|
||||
|
||||
tr_magnetFree(info);
|
||||
info = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MAIN_SINGLE_TEST(test1)
|
|
@ -12,9 +12,12 @@
|
|||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
#include "tr-macros.h"
|
||||
#include "transmission.h"
|
||||
#include "variant.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
typedef struct tr_magnet_info
|
||||
{
|
||||
uint8_t hash[20];
|
||||
|
@ -36,3 +39,5 @@ struct tr_variant;
|
|||
void tr_magnetCreateMetainfo(tr_magnet_info const*, tr_variant*);
|
||||
|
||||
void tr_magnetFree(tr_magnet_info* info);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,288 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
#include "transmission.h"
|
||||
#include "crypto-utils.h"
|
||||
#include "file.h"
|
||||
#include "makemeta.h"
|
||||
|
||||
#include <stdlib.h> /* mktemp() */
|
||||
#include <string.h> /* strlen() */
|
||||
|
||||
static int test_single_file_impl(tr_tracker_info const* trackers, size_t const trackerCount, void const* payload,
|
||||
size_t const payloadSize, char const* comment, bool isPrivate)
|
||||
{
|
||||
char* sandbox;
|
||||
char* input_file;
|
||||
char* torrent_file;
|
||||
tr_metainfo_builder* builder;
|
||||
tr_ctor* ctor;
|
||||
tr_parse_result parse_result;
|
||||
tr_info inf;
|
||||
char* tmpstr;
|
||||
|
||||
/* set up our local test sandbox */
|
||||
sandbox = libtest_sandbox_create();
|
||||
|
||||
/* create a single input file */
|
||||
input_file = tr_buildPath(sandbox, "test.XXXXXX", NULL);
|
||||
libtest_create_tmpfile_with_contents(input_file, payload, payloadSize);
|
||||
builder = tr_metaInfoBuilderCreate(input_file);
|
||||
check_str(builder->top, ==, input_file);
|
||||
check_int(builder->fileCount, ==, 1);
|
||||
check_str(builder->files[0].filename, ==, input_file);
|
||||
check_int(builder->files[0].size, ==, payloadSize);
|
||||
check_int(builder->totalSize, ==, payloadSize);
|
||||
check(!builder->isFolder);
|
||||
check(!builder->abortFlag);
|
||||
|
||||
/* have tr_makeMetaInfo() build the .torrent file */
|
||||
torrent_file = tr_strdup_printf("%s.torrent", input_file);
|
||||
tr_makeMetaInfo(builder, torrent_file, trackers, trackerCount, comment, isPrivate);
|
||||
check_bool(isPrivate, ==, builder->isPrivate);
|
||||
check_str(builder->outputFile, ==, torrent_file);
|
||||
check_str(builder->comment, ==, comment);
|
||||
check_int(builder->trackerCount, ==, trackerCount);
|
||||
|
||||
while (!builder->isDone)
|
||||
{
|
||||
tr_wait_msec(100);
|
||||
}
|
||||
|
||||
/* now let's check our work: parse the .torrent file */
|
||||
ctor = tr_ctorNew(NULL);
|
||||
libttest_sync();
|
||||
tr_ctorSetMetainfoFromFile(ctor, torrent_file);
|
||||
parse_result = tr_torrentParse(ctor, &inf);
|
||||
check_int(parse_result, ==, TR_PARSE_OK);
|
||||
|
||||
/* quick check of some of the parsed metainfo */
|
||||
check_int(inf.totalSize, ==, payloadSize);
|
||||
tmpstr = tr_sys_path_basename(input_file, NULL);
|
||||
check_str(inf.name, ==, tmpstr);
|
||||
tr_free(tmpstr);
|
||||
check_str(inf.comment, ==, comment);
|
||||
check_int(inf.fileCount, ==, 1);
|
||||
check_int(inf.isPrivate, ==, isPrivate);
|
||||
check(!inf.isFolder);
|
||||
check_int(inf.trackerCount, ==, trackerCount);
|
||||
|
||||
/* cleanup */
|
||||
tr_free(torrent_file);
|
||||
tr_free(input_file);
|
||||
tr_ctorFree(ctor);
|
||||
tr_metainfoFree(&inf);
|
||||
tr_metaInfoBuilderFree(builder);
|
||||
libtest_sandbox_destroy(sandbox);
|
||||
tr_free(sandbox);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_single_file(void)
|
||||
{
|
||||
tr_tracker_info trackers[16];
|
||||
size_t trackerCount;
|
||||
bool isPrivate;
|
||||
char const* comment;
|
||||
char const* payload;
|
||||
size_t payloadSize;
|
||||
|
||||
trackerCount = 0;
|
||||
trackers[trackerCount].tier = trackerCount;
|
||||
trackers[trackerCount].announce = (char*)"udp://tracker.openbittorrent.com:80";
|
||||
++trackerCount;
|
||||
trackers[trackerCount].tier = trackerCount;
|
||||
trackers[trackerCount].announce = (char*)"udp://tracker.publicbt.com:80";
|
||||
++trackerCount;
|
||||
payload = "Hello, World!\n";
|
||||
payloadSize = strlen(payload);
|
||||
comment = "This is the comment";
|
||||
isPrivate = false;
|
||||
test_single_file_impl(trackers, trackerCount, payload, payloadSize, comment, isPrivate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_single_directory_impl(tr_tracker_info const* trackers, size_t const trackerCount, void const** payloads,
|
||||
size_t const* payloadSizes, size_t const payloadCount, char const* comment,
|
||||
bool const isPrivate)
|
||||
{
|
||||
char* sandbox;
|
||||
char* torrent_file;
|
||||
tr_metainfo_builder* builder;
|
||||
tr_ctor* ctor;
|
||||
tr_parse_result parse_result;
|
||||
tr_info inf;
|
||||
char* top;
|
||||
char** files;
|
||||
size_t totalSize;
|
||||
char* tmpstr;
|
||||
|
||||
/* set up our local test sandbox */
|
||||
sandbox = libtest_sandbox_create();
|
||||
|
||||
/* create the top temp directory */
|
||||
top = tr_buildPath(sandbox, "folder.XXXXXX", NULL);
|
||||
tr_sys_dir_create_temp(top, NULL);
|
||||
|
||||
/* build the payload files that go into the top temp directory */
|
||||
files = tr_new(char*, payloadCount);
|
||||
totalSize = 0;
|
||||
|
||||
for (size_t i = 0; i < payloadCount; i++)
|
||||
{
|
||||
char tmpl[16];
|
||||
tr_snprintf(tmpl, sizeof(tmpl), "file.%04zu%s", i, "XXXXXX");
|
||||
files[i] = tr_buildPath(top, tmpl, NULL);
|
||||
libtest_create_tmpfile_with_contents(files[i], payloads[i], payloadSizes[i]);
|
||||
totalSize += payloadSizes[i];
|
||||
}
|
||||
|
||||
libttest_sync();
|
||||
|
||||
/* init the builder */
|
||||
builder = tr_metaInfoBuilderCreate(top);
|
||||
check(!builder->abortFlag);
|
||||
check_str(builder->top, ==, top);
|
||||
check_int(builder->fileCount, ==, payloadCount);
|
||||
check_int(builder->totalSize, ==, totalSize);
|
||||
check(builder->isFolder);
|
||||
|
||||
for (size_t i = 0; i < builder->fileCount; i++)
|
||||
{
|
||||
check_str(builder->files[i].filename, ==, files[i]);
|
||||
check_int(builder->files[i].size, ==, payloadSizes[i]);
|
||||
}
|
||||
|
||||
/* call tr_makeMetaInfo() to build the .torrent file */
|
||||
torrent_file = tr_strdup_printf("%s.torrent", top);
|
||||
tr_makeMetaInfo(builder, torrent_file, trackers, trackerCount, comment, isPrivate);
|
||||
check_bool(isPrivate, ==, builder->isPrivate);
|
||||
check_str(builder->outputFile, ==, torrent_file);
|
||||
check_str(builder->comment, ==, comment);
|
||||
check_int(builder->trackerCount, ==, trackerCount);
|
||||
|
||||
while (!builder->isDone)
|
||||
{
|
||||
tr_wait_msec(100);
|
||||
}
|
||||
|
||||
/* now let's check our work: parse the .torrent file */
|
||||
ctor = tr_ctorNew(NULL);
|
||||
libttest_sync();
|
||||
tr_ctorSetMetainfoFromFile(ctor, torrent_file);
|
||||
parse_result = tr_torrentParse(ctor, &inf);
|
||||
check_int(parse_result, ==, TR_PARSE_OK);
|
||||
|
||||
/* quick check of some of the parsed metainfo */
|
||||
check_int(inf.totalSize, ==, totalSize);
|
||||
tmpstr = tr_sys_path_basename(top, NULL);
|
||||
check_str(inf.name, ==, tmpstr);
|
||||
tr_free(tmpstr);
|
||||
check_str(inf.comment, ==, comment);
|
||||
check_int(inf.fileCount, ==, payloadCount);
|
||||
check_int(inf.isPrivate, ==, isPrivate);
|
||||
check_int(inf.isFolder, ==, builder->isFolder);
|
||||
check_int(inf.trackerCount, ==, trackerCount);
|
||||
|
||||
/* cleanup */
|
||||
tr_free(torrent_file);
|
||||
tr_ctorFree(ctor);
|
||||
tr_metainfoFree(&inf);
|
||||
tr_metaInfoBuilderFree(builder);
|
||||
|
||||
for (size_t i = 0; i < payloadCount; i++)
|
||||
{
|
||||
tr_free(files[i]);
|
||||
}
|
||||
|
||||
tr_free(files);
|
||||
libtest_sandbox_destroy(sandbox);
|
||||
tr_free(sandbox);
|
||||
tr_free(top);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_single_directory_random_payload_impl(tr_tracker_info const* trackers, size_t const trackerCount,
|
||||
size_t const maxFileCount, size_t const maxFileSize, char const* comment,
|
||||
bool const isPrivate)
|
||||
{
|
||||
void** payloads;
|
||||
size_t* payloadSizes;
|
||||
size_t payloadCount;
|
||||
|
||||
/* build random payloads */
|
||||
payloadCount = 1 + tr_rand_int_weak(maxFileCount);
|
||||
payloads = tr_new0(void*, payloadCount);
|
||||
payloadSizes = tr_new0(size_t, payloadCount);
|
||||
|
||||
for (size_t i = 0; i < payloadCount; i++)
|
||||
{
|
||||
size_t const n = 1 + tr_rand_int_weak(maxFileSize);
|
||||
payloads[i] = tr_new(char, n);
|
||||
tr_rand_buffer(payloads[i], n);
|
||||
payloadSizes[i] = n;
|
||||
}
|
||||
|
||||
/* run the test */
|
||||
test_single_directory_impl(trackers, trackerCount, (void const**)payloads, payloadSizes, payloadCount, comment, isPrivate);
|
||||
|
||||
/* cleanup */
|
||||
for (size_t i = 0; i < payloadCount; i++)
|
||||
{
|
||||
tr_free(payloads[i]);
|
||||
}
|
||||
|
||||
tr_free(payloads);
|
||||
tr_free(payloadSizes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DEFAULT_MAX_FILE_COUNT 16
|
||||
#define DEFAULT_MAX_FILE_SIZE 1024
|
||||
|
||||
static int test_single_directory_random_payload(void)
|
||||
{
|
||||
tr_tracker_info trackers[16];
|
||||
size_t trackerCount;
|
||||
bool isPrivate;
|
||||
char const* comment;
|
||||
|
||||
trackerCount = 0;
|
||||
trackers[trackerCount].tier = trackerCount;
|
||||
trackers[trackerCount].announce = (char*)"udp://tracker.openbittorrent.com:80";
|
||||
++trackerCount;
|
||||
trackers[trackerCount].tier = trackerCount;
|
||||
trackers[trackerCount].announce = (char*)"udp://tracker.publicbt.com:80";
|
||||
++trackerCount;
|
||||
comment = "This is the comment";
|
||||
isPrivate = false;
|
||||
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
{
|
||||
test_single_directory_random_payload_impl(trackers, trackerCount, DEFAULT_MAX_FILE_COUNT, DEFAULT_MAX_FILE_SIZE,
|
||||
comment, isPrivate);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testFunc const tests[] =
|
||||
{
|
||||
test_single_file,
|
||||
test_single_directory_random_payload
|
||||
};
|
||||
|
||||
return runTests(tests, NUM_TESTS(tests));
|
||||
}
|
|
@ -43,12 +43,11 @@ static struct FileList* getFiles(char const* dir, char const* base, struct FileL
|
|||
return NULL;
|
||||
}
|
||||
|
||||
char* buf;
|
||||
char* buf = tr_buildPath(dir, base, NULL);
|
||||
tr_sys_path_native_separators(buf);
|
||||
|
||||
tr_sys_path_info info;
|
||||
tr_error* error = NULL;
|
||||
|
||||
buf = tr_buildPath(dir, base, NULL);
|
||||
|
||||
if (!tr_sys_path_get_info(buf, 0, &info, &error))
|
||||
{
|
||||
tr_logAddError(_("Torrent Creator is skipping file \"%s\": %s"), buf, error->message);
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "tr-macros.h"
|
||||
#include "transmission.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
typedef struct tr_metainfo_builder_file
|
||||
{
|
||||
|
@ -117,6 +117,4 @@ void tr_metaInfoBuilderFree(tr_metainfo_builder*);
|
|||
void tr_makeMetaInfo(tr_metainfo_builder* builder, char const* outputFile, tr_tracker_info const* trackers, int trackerCount,
|
||||
char const* comment, bool isPrivate);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -595,14 +595,14 @@ static char const* tr_metainfoParseImpl(tr_session const* session, tr_info* inf,
|
|||
}
|
||||
else
|
||||
{
|
||||
size_t len;
|
||||
char* bstr = tr_variantToStr(infoDict, TR_VARIANT_FMT_BENC, &len);
|
||||
tr_sha1(inf->hash, bstr, (int)len, NULL);
|
||||
size_t blen;
|
||||
char* bstr = tr_variantToStr(infoDict, TR_VARIANT_FMT_BENC, &blen);
|
||||
tr_sha1(inf->hash, bstr, (int)blen, NULL);
|
||||
tr_sha1_to_hex(inf->hashString, inf->hash);
|
||||
|
||||
if (infoDictLength != NULL)
|
||||
{
|
||||
*infoDictLength = len;
|
||||
*infoDictLength = blen;
|
||||
}
|
||||
|
||||
tr_free(bstr);
|
||||
|
@ -706,9 +706,9 @@ static char const* tr_metainfoParseImpl(tr_session const* session, tr_info* inf,
|
|||
inf->pieceCount = len / SHA_DIGEST_LENGTH;
|
||||
inf->pieces = tr_new0(tr_piece, inf->pieceCount);
|
||||
|
||||
for (tr_piece_index_t i = 0; i < inf->pieceCount; i++)
|
||||
for (tr_piece_index_t pi = 0; pi < inf->pieceCount; ++pi)
|
||||
{
|
||||
memcpy(inf->pieces[i].hash, &raw[i * SHA_DIGEST_LENGTH], SHA_DIGEST_LENGTH);
|
||||
memcpy(inf->pieces[pi].hash, &raw[pi * SHA_DIGEST_LENGTH], SHA_DIGEST_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
|
||||
#include "transmission.h"
|
||||
#include "variant.h"
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
enum tr_metainfo_basename_format
|
||||
{
|
||||
|
@ -33,3 +36,5 @@ void tr_metainfoMigrateFile(tr_session const* session, tr_info const* info, enum
|
|||
|
||||
/** @brief Private function that's exposed here only for unit tests */
|
||||
char* tr_metainfo_sanitize_path_component(char const* str, size_t len, bool* is_adjusted);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,240 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h> /* strcmp() */
|
||||
#include <stdio.h>
|
||||
|
||||
#include <event2/buffer.h>
|
||||
|
||||
#include "transmission.h"
|
||||
#include "cache.h"
|
||||
#include "file.h"
|
||||
#include "resume.h"
|
||||
#include "trevent.h"
|
||||
#include "torrent.h" /* tr_isTorrent() */
|
||||
#include "variant.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static void zeroes_completeness_func(tr_torrent* torrent UNUSED, tr_completeness completeness, bool wasRunning UNUSED,
|
||||
void* user_data)
|
||||
{
|
||||
*(tr_completeness*)user_data = completeness;
|
||||
}
|
||||
|
||||
#define check_file_location(tor, i, expected_path) \
|
||||
do \
|
||||
{ \
|
||||
char* path = tr_torrentFindFile(tor, i); \
|
||||
char* expected = expected_path; \
|
||||
check_str(path, ==, expected); \
|
||||
tr_free(expected); \
|
||||
tr_free(path); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
struct test_incomplete_dir_data
|
||||
{
|
||||
tr_session* session;
|
||||
tr_torrent* tor;
|
||||
tr_block_index_t block;
|
||||
tr_piece_index_t pieceIndex;
|
||||
uint32_t offset;
|
||||
struct evbuffer* buf;
|
||||
bool done;
|
||||
};
|
||||
|
||||
static void test_incomplete_dir_threadfunc(void* vdata)
|
||||
{
|
||||
struct test_incomplete_dir_data* data = vdata;
|
||||
tr_cacheWriteBlock(data->session->cache, data->tor, 0, data->offset, data->tor->blockSize, data->buf);
|
||||
tr_torrentGotBlock(data->tor, data->block);
|
||||
data->done = true;
|
||||
}
|
||||
|
||||
static int test_incomplete_dir_impl(char const* incomplete_dir, char const* download_dir)
|
||||
{
|
||||
tr_session* session;
|
||||
tr_torrent* tor;
|
||||
tr_completeness completeness;
|
||||
tr_completeness const completeness_unset = -1;
|
||||
time_t const deadline = time(NULL) + 300;
|
||||
tr_variant settings;
|
||||
|
||||
/* init the session */
|
||||
tr_variantInitDict(&settings, 3);
|
||||
tr_variantDictAddStr(&settings, TR_KEY_download_dir, download_dir);
|
||||
tr_variantDictAddStr(&settings, TR_KEY_incomplete_dir, incomplete_dir);
|
||||
tr_variantDictAddBool(&settings, TR_KEY_incomplete_dir_enabled, true);
|
||||
session = libttest_session_init(&settings);
|
||||
tr_variantFree(&settings);
|
||||
download_dir = tr_sessionGetDownloadDir(session);
|
||||
incomplete_dir = tr_sessionGetIncompleteDir(session);
|
||||
|
||||
/* init an incomplete torrent.
|
||||
the test zero_torrent will be missing its first piece */
|
||||
tor = libttest_zero_torrent_init(session);
|
||||
libttest_zero_torrent_populate(tor, false);
|
||||
check_uint(tr_torrentStat(tor)->leftUntilDone, ==, tor->info.pieceSize);
|
||||
check_file_location(tor, 0, tr_strdup_printf("%s/%s.part", incomplete_dir, tor->info.files[0].name));
|
||||
check_file_location(tor, 1, tr_buildPath(incomplete_dir, tor->info.files[1].name, NULL));
|
||||
check_uint(tr_torrentStat(tor)->leftUntilDone, ==, tor->info.pieceSize);
|
||||
|
||||
completeness = completeness_unset;
|
||||
tr_torrentSetCompletenessCallback(tor, zeroes_completeness_func, &completeness);
|
||||
|
||||
/* now finish writing it */
|
||||
{
|
||||
tr_block_index_t first;
|
||||
tr_block_index_t last;
|
||||
char* zero_block = tr_new0(char, tor->blockSize);
|
||||
struct test_incomplete_dir_data data;
|
||||
|
||||
data.session = session;
|
||||
data.tor = tor;
|
||||
data.pieceIndex = 0;
|
||||
data.buf = evbuffer_new();
|
||||
|
||||
tr_torGetPieceBlockRange(tor, data.pieceIndex, &first, &last);
|
||||
|
||||
for (tr_block_index_t block_index = first; block_index <= last; ++block_index)
|
||||
{
|
||||
evbuffer_add(data.buf, zero_block, tor->blockSize);
|
||||
data.block = block_index;
|
||||
data.done = false;
|
||||
data.offset = data.block * tor->blockSize;
|
||||
tr_runInEventThread(session, test_incomplete_dir_threadfunc, &data);
|
||||
|
||||
do
|
||||
{
|
||||
tr_wait_msec(50);
|
||||
}
|
||||
while (!data.done);
|
||||
}
|
||||
|
||||
evbuffer_free(data.buf);
|
||||
tr_free(zero_block);
|
||||
}
|
||||
|
||||
libttest_blockingTorrentVerify(tor);
|
||||
check_uint(tr_torrentStat(tor)->leftUntilDone, ==, 0);
|
||||
|
||||
while (completeness == completeness_unset && time(NULL) <= deadline)
|
||||
{
|
||||
tr_wait_msec(50);
|
||||
}
|
||||
|
||||
check_int(completeness, ==, TR_SEED);
|
||||
|
||||
for (tr_file_index_t file_index = 0; file_index < tor->info.fileCount; ++file_index)
|
||||
{
|
||||
check_file_location(tor, file_index, tr_buildPath(download_dir, tor->info.files[file_index].name, NULL));
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
tr_torrentRemove(tor, true, tr_sys_path_remove);
|
||||
libttest_session_close(session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_incomplete_dir(void)
|
||||
{
|
||||
int rv;
|
||||
|
||||
/* test what happens when incompleteDir is a subdir of downloadDir*/
|
||||
if ((rv = test_incomplete_dir_impl("Downloads/Incomplete", "Downloads")) != 0)
|
||||
{
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* test what happens when downloadDir is a subdir of incompleteDir */
|
||||
if ((rv = test_incomplete_dir_impl("Downloads", "Downloads/Complete")) != 0)
|
||||
{
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* test what happens when downloadDir and incompleteDir are siblings */
|
||||
if ((rv = test_incomplete_dir_impl("Incomplete", "Downloads")) != 0)
|
||||
{
|
||||
return rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static int test_set_location(void)
|
||||
{
|
||||
int state;
|
||||
char* target_dir;
|
||||
tr_torrent* tor;
|
||||
tr_session* session;
|
||||
time_t const deadline = time(NULL) + 300;
|
||||
|
||||
/* init the session */
|
||||
session = libttest_session_init(NULL);
|
||||
target_dir = tr_buildPath(tr_sessionGetConfigDir(session), "target", NULL);
|
||||
tr_sys_dir_create(target_dir, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
|
||||
|
||||
/* init a torrent. */
|
||||
tor = libttest_zero_torrent_init(session);
|
||||
libttest_zero_torrent_populate(tor, true);
|
||||
libttest_blockingTorrentVerify(tor);
|
||||
check_uint(tr_torrentStat(tor)->leftUntilDone, ==, 0);
|
||||
|
||||
/* now move it */
|
||||
state = -1;
|
||||
tr_torrentSetLocation(tor, target_dir, true, NULL, &state);
|
||||
|
||||
while (state == TR_LOC_MOVING && time(NULL) <= deadline)
|
||||
{
|
||||
tr_wait_msec(50);
|
||||
}
|
||||
|
||||
check_int(state, ==, TR_LOC_DONE);
|
||||
|
||||
/* confirm the torrent is still complete after being moved */
|
||||
libttest_blockingTorrentVerify(tor);
|
||||
check_uint(tr_torrentStat(tor)->leftUntilDone, ==, 0);
|
||||
|
||||
/* confirm the filest really got moved */
|
||||
libttest_sync();
|
||||
|
||||
for (tr_file_index_t file_index = 0; file_index < tor->info.fileCount; ++file_index)
|
||||
{
|
||||
check_file_location(tor, file_index, tr_buildPath(target_dir, tor->info.files[file_index].name, NULL));
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
tr_free(target_dir);
|
||||
tr_torrentRemove(tor, true, tr_sys_path_remove);
|
||||
libttest_session_close(session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testFunc const tests[] =
|
||||
{
|
||||
test_incomplete_dir,
|
||||
test_set_location
|
||||
};
|
||||
|
||||
return runTests(tests, NUM_TESTS(tests));
|
||||
}
|
|
@ -168,11 +168,11 @@ int tr_natpmpPulse(struct tr_natpmp* nat, tr_port private_port, bool is_enabled,
|
|||
|
||||
if (val >= 0)
|
||||
{
|
||||
int const private_port = resp.pnu.newportmapping.privateport;
|
||||
int const unmapped_port = resp.pnu.newportmapping.privateport;
|
||||
|
||||
tr_logAddNamedInfo(getKey(), _("no longer forwarding port %d"), private_port);
|
||||
tr_logAddNamedInfo(getKey(), _("no longer forwarding port %d"), unmapped_port);
|
||||
|
||||
if (nat->private_port == private_port)
|
||||
if (nat->private_port == unmapped_port)
|
||||
{
|
||||
nat->private_port = 0;
|
||||
nat->public_port = 0;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#include <event2/util.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <libutp/utp.h>
|
||||
|
||||
#include "transmission.h"
|
||||
|
@ -766,3 +767,17 @@ bool tr_address_is_valid_for_peers(tr_address const* addr, tr_port port)
|
|||
return port != 0 && tr_address_is_valid(addr) && !isIPv6LinkLocalAddress(addr) && !isIPv4MappedAddress(addr) &&
|
||||
!isMartianAddr(addr);
|
||||
}
|
||||
|
||||
struct tr_peer_socket tr_peer_socket_tcp_create(tr_socket_t const handle)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SOCKET);
|
||||
struct tr_peer_socket const ret = { .type = TR_PEER_SOCKET_TYPE_TCP, .handle.tcp = handle };
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct tr_peer_socket tr_peer_socket_utp_create(struct UTPSocket* const handle)
|
||||
{
|
||||
TR_ASSERT(handle != NULL);
|
||||
struct tr_peer_socket const ret = { .type = TR_PEER_SOCKET_TYPE_UTP, .handle.utp = handle };
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,10 @@
|
|||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef SOCKET tr_socket_t;
|
||||
#define TR_BAD_SOCKET INVALID_SOCKET
|
||||
|
@ -129,10 +133,6 @@ enum
|
|||
|
||||
struct tr_session;
|
||||
|
||||
struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const* addr, tr_port port, bool clientIsSeed);
|
||||
|
||||
struct tr_peer_socket tr_netOpenPeerUTPSocket(tr_session* session, tr_address const* addr, tr_port port, bool clientIsSeed);
|
||||
|
||||
tr_socket_t tr_netBindTCP(tr_address const* addr, tr_port port, bool suppressMsgs);
|
||||
|
||||
tr_socket_t tr_netAccept(tr_session* session, tr_socket_t bound, tr_address* setme_addr, tr_port* setme_port);
|
||||
|
@ -154,3 +154,5 @@ bool tr_net_hasIPv6(tr_port);
|
|||
char* tr_net_strerror(char* buf, size_t buflen, int err);
|
||||
|
||||
unsigned char const* tr_globalIPv6(void);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <event2/buffer.h>
|
||||
#include <event2/bufferevent.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <libutp/utp.h>
|
||||
|
||||
#include "transmission.h"
|
||||
|
|
|
@ -113,15 +113,15 @@ tr_peerIo* tr_peerIoNewOutgoing(tr_session* session, struct tr_bandwidth* parent
|
|||
uint8_t const* torrentHash, bool isSeed, bool utp);
|
||||
|
||||
tr_peerIo* tr_peerIoNewIncoming(tr_session* session, struct tr_bandwidth* parent, struct tr_address const* addr, tr_port port,
|
||||
struct tr_peer_socket socket);
|
||||
struct tr_peer_socket const socket);
|
||||
|
||||
void tr_peerIoRefImpl(char const* file, int line, tr_peerIo* io);
|
||||
|
||||
#define tr_peerIoRef(io) tr_peerIoRefImpl(__FILE__, __LINE__, (io));
|
||||
#define tr_peerIoRef(io) tr_peerIoRefImpl(__FILE__, __LINE__, (io))
|
||||
|
||||
void tr_peerIoUnrefImpl(char const* file, int line, tr_peerIo* io);
|
||||
|
||||
#define tr_peerIoUnref(io) tr_peerIoUnrefImpl(__FILE__, __LINE__, (io));
|
||||
#define tr_peerIoUnref(io) tr_peerIoUnrefImpl(__FILE__, __LINE__, (io))
|
||||
|
||||
#define PEER_IO_MAGIC_NUMBER 206745
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <event2/event.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <libutp/utp.h>
|
||||
|
||||
#include "transmission.h"
|
||||
|
@ -60,8 +61,6 @@ enum
|
|||
/* max number of peers to ask for per second overall.
|
||||
* this throttle is to avoid overloading the router */
|
||||
MAX_CONNECTIONS_PER_SECOND = 12,
|
||||
/* */
|
||||
MAX_CONNECTIONS_PER_PULSE = (int)(MAX_CONNECTIONS_PER_SECOND * (RECONNECT_PERIOD_MSEC / 1000.0)),
|
||||
/* number of bad pieces a peer is allowed to send before we ban them */
|
||||
MAX_BAD_PIECES_PER_PEER = 5,
|
||||
/* amount of time to keep a list of request pieces lying around
|
||||
|
@ -2098,7 +2097,6 @@ static bool myHandshakeDoneCB(tr_handshake* handshake, tr_peerIo* io, bool readA
|
|||
else
|
||||
{
|
||||
tr_quark client;
|
||||
tr_peerIo* io;
|
||||
char buf[128];
|
||||
|
||||
if (peer_id != NULL)
|
||||
|
@ -2110,9 +2108,9 @@ static bool myHandshakeDoneCB(tr_handshake* handshake, tr_peerIo* io, bool readA
|
|||
client = TR_KEY_NONE;
|
||||
}
|
||||
|
||||
io = tr_handshakeStealIO(handshake); /* this steals its refcount too, which is balanced by our unref in peerDelete() */
|
||||
tr_peerIoSetParent(io, &s->tor->bandwidth);
|
||||
createBitTorrentPeer(s->tor, io, atom, client);
|
||||
tr_peerIo* stolen = tr_handshakeStealIO(handshake); /* this steals its refcount too, which is balanced by our unref in peerDelete() */
|
||||
tr_peerIoSetParent(stolen, &s->tor->bandwidth);
|
||||
createBitTorrentPeer(s->tor, stolen, atom, client);
|
||||
|
||||
success = true;
|
||||
}
|
||||
|
@ -2559,17 +2557,17 @@ void tr_peerUpdateProgress(tr_torrent* tor, tr_peer* peer)
|
|||
}
|
||||
|
||||
/* clamp the progress range */
|
||||
if (peer->progress < 0.0)
|
||||
if (peer->progress < 0.0f)
|
||||
{
|
||||
peer->progress = 0.0;
|
||||
peer->progress = 0.0f;
|
||||
}
|
||||
|
||||
if (peer->progress > 1.0)
|
||||
if (peer->progress > 1.0f)
|
||||
{
|
||||
peer->progress = 1.0;
|
||||
peer->progress = 1.0f;
|
||||
}
|
||||
|
||||
if (peer->atom != NULL && peer->progress >= 1.0)
|
||||
if (peer->atom != NULL && peer->progress >= 1.0f)
|
||||
{
|
||||
atomSetSeed(tor->swarm, peer->atom);
|
||||
}
|
||||
|
@ -2699,9 +2697,9 @@ uint64_t tr_peerMgrGetDesiredAvailable(tr_torrent const* tor)
|
|||
return 0;
|
||||
}
|
||||
|
||||
size_t const n = tr_ptrArraySize(&s->peers);
|
||||
size_t const peer_count = tr_ptrArraySize(&s->peers);
|
||||
|
||||
if (n == 0)
|
||||
if (peer_count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -2709,7 +2707,7 @@ uint64_t tr_peerMgrGetDesiredAvailable(tr_torrent const* tor)
|
|||
{
|
||||
tr_peer const** peers = (tr_peer const**)tr_ptrArrayBase(&s->peers);
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
for (size_t i = 0; i < peer_count; ++i)
|
||||
{
|
||||
if (peers[i]->atom != NULL && atomIsSeed(peers[i]->atom))
|
||||
{
|
||||
|
@ -3779,6 +3777,7 @@ static void reconnectPulse(evutil_socket_t foo UNUSED, short bar UNUSED, void* v
|
|||
}
|
||||
|
||||
/* try to make new peer connections */
|
||||
int const MAX_CONNECTIONS_PER_PULSE = (int)(MAX_CONNECTIONS_PER_SECOND * (RECONNECT_PERIOD_MSEC / 1000.0));
|
||||
makeNewPeerConnections(mgr, MAX_CONNECTIONS_PER_PULSE);
|
||||
}
|
||||
|
||||
|
@ -4002,7 +4001,7 @@ static void atomPulse(evutil_socket_t foo UNUSED, short bar UNUSED, void* vmgr)
|
|||
s->pool = TR_PTR_ARRAY_INIT;
|
||||
qsort(keep, keepCount, sizeof(struct peer_atom*), compareAtomPtrsByAddress);
|
||||
|
||||
for (int i = 0; i < keepCount; ++i)
|
||||
for (i = 0; i < keepCount; ++i)
|
||||
{
|
||||
tr_ptrArrayAppend(&s->pool, keep[i]);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "net.h" /* tr_address */
|
||||
#include "peer-common.h"
|
||||
#include "peer-socket.h"
|
||||
#include "quark.h"
|
||||
|
||||
/**
|
||||
|
@ -87,7 +88,7 @@ bool tr_peerMgrDidPeerRequest(tr_torrent const* torrent, tr_peer const* peer, tr
|
|||
|
||||
void tr_peerMgrRebuildRequests(tr_torrent* torrent);
|
||||
|
||||
void tr_peerMgrAddIncoming(tr_peerMgr* manager, tr_address* addr, tr_port port, struct tr_peer_socket socket);
|
||||
void tr_peerMgrAddIncoming(tr_peerMgr* manager, tr_address* addr, tr_port port, struct tr_peer_socket const socket);
|
||||
|
||||
tr_pex* tr_peerMgrCompactToPex(void const* compact, size_t compactLen, uint8_t const* added_f, size_t added_f_len,
|
||||
size_t* setme_pex_count);
|
||||
|
|
|
@ -1117,15 +1117,15 @@ static void parseUtMetadata(tr_peerMsgs* msgs, uint32_t msglen, struct evbuffer*
|
|||
}
|
||||
else
|
||||
{
|
||||
tr_variant tmp;
|
||||
tr_variant v;
|
||||
struct evbuffer* payload;
|
||||
struct evbuffer* out = msgs->outMessages;
|
||||
|
||||
/* build the rejection message */
|
||||
tr_variantInitDict(&tmp, 2);
|
||||
tr_variantDictAddInt(&tmp, TR_KEY_msg_type, METADATA_MSG_TYPE_REJECT);
|
||||
tr_variantDictAddInt(&tmp, TR_KEY_piece, piece);
|
||||
payload = tr_variantToBuf(&tmp, TR_VARIANT_FMT_BENC);
|
||||
tr_variantInitDict(&v, 2);
|
||||
tr_variantDictAddInt(&v, TR_KEY_msg_type, METADATA_MSG_TYPE_REJECT);
|
||||
tr_variantDictAddInt(&v, TR_KEY_piece, piece);
|
||||
payload = tr_variantToBuf(&v, TR_VARIANT_FMT_BENC);
|
||||
|
||||
/* write it out as a LTEP message to our outMessages buffer */
|
||||
evbuffer_add_uint32(out, 2 * sizeof(uint8_t) + evbuffer_get_length(payload));
|
||||
|
@ -1137,7 +1137,7 @@ static void parseUtMetadata(tr_peerMsgs* msgs, uint32_t msglen, struct evbuffer*
|
|||
|
||||
/* cleanup */
|
||||
evbuffer_free(payload);
|
||||
tr_variantFree(&tmp);
|
||||
tr_variantFree(&v);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
|
||||
#include "net.h"
|
||||
#include "tr-assert.h"
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
enum tr_peer_socket_type
|
||||
{
|
||||
|
@ -36,14 +39,15 @@ struct tr_peer_socket
|
|||
|
||||
#define TR_PEER_SOCKET_INIT ((struct tr_peer_socket){ .type = TR_PEER_SOCKET_TYPE_NONE })
|
||||
|
||||
static inline struct tr_peer_socket tr_peer_socket_tcp_create(tr_socket_t const handle)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SOCKET);
|
||||
return (struct tr_peer_socket){ .type = TR_PEER_SOCKET_TYPE_TCP, .handle.tcp = handle };
|
||||
}
|
||||
struct tr_peer_socket tr_peer_socket_tcp_create(tr_socket_t const handle);
|
||||
|
||||
static inline struct tr_peer_socket tr_peer_socket_utp_create(struct UTPSocket* const handle)
|
||||
{
|
||||
TR_ASSERT(handle != NULL);
|
||||
return (struct tr_peer_socket){ .type = TR_PEER_SOCKET_TYPE_UTP, .handle.utp = handle };
|
||||
}
|
||||
struct tr_peer_socket tr_peer_socket_utp_create(struct UTPSocket* const handle);
|
||||
|
||||
struct tr_session;
|
||||
struct tr_address;
|
||||
|
||||
struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const* addr, tr_port port, bool clientIsSeed);
|
||||
|
||||
struct tr_peer_socket tr_netOpenPeerUTPSocket(tr_session* session, tr_address const* addr, tr_port port, bool clientIsSeed);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h> /* strlen() */
|
||||
|
||||
#include "transmission.h"
|
||||
#include "quark.h"
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
static int test_static_quarks(void)
|
||||
{
|
||||
for (int i = 0; i < TR_N_KEYS; i++)
|
||||
{
|
||||
tr_quark q;
|
||||
size_t len;
|
||||
char const* str;
|
||||
|
||||
str = tr_quark_get_string((tr_quark)i, &len);
|
||||
check_uint(len, ==, strlen(str));
|
||||
check(tr_quark_lookup(str, len, &q));
|
||||
check_int((int)q, ==, i);
|
||||
}
|
||||
|
||||
for (int i = 0; i + 1 < TR_N_KEYS; i++)
|
||||
{
|
||||
size_t len1;
|
||||
size_t len2;
|
||||
char const* str1;
|
||||
char const* str2;
|
||||
|
||||
str1 = tr_quark_get_string((tr_quark)i, &len1);
|
||||
str2 = tr_quark_get_string((tr_quark)(i + 1), &len2);
|
||||
|
||||
check_str(str1, <, str2);
|
||||
}
|
||||
|
||||
tr_quark const q = tr_quark_new(NULL, TR_BAD_SIZE);
|
||||
check_int((int)q, ==, TR_KEY_NONE);
|
||||
check_str(tr_quark_get_string(q, NULL), ==, "");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MAIN_SINGLE_TEST(test_static_quarks)
|
|
@ -8,10 +8,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
/* Quarks — a 2-way association between a string and a unique integer identifier */
|
||||
typedef size_t tr_quark;
|
||||
|
@ -435,6 +434,4 @@ tr_quark tr_quark_new(void const* str, size_t len);
|
|||
****
|
||||
***/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,587 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h> /* fopen() */
|
||||
#include <string.h> /* strcmp() */
|
||||
|
||||
#include "transmission.h"
|
||||
#include "crypto-utils.h"
|
||||
#include "file.h"
|
||||
#include "resume.h"
|
||||
#include "torrent.h" /* tr_isTorrent() */
|
||||
#include "tr-assert.h"
|
||||
#include "variant.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static tr_session* session = NULL;
|
||||
|
||||
#define check_have_none(tor, totalSize) \
|
||||
do \
|
||||
{ \
|
||||
tr_stat const* tst = tr_torrentStat(tor); \
|
||||
check_int(tst->activity, ==, TR_STATUS_STOPPED); \
|
||||
check_int(tst->error, ==, TR_STAT_OK); \
|
||||
check_uint(tst->sizeWhenDone, ==, totalSize); \
|
||||
check_uint(tst->leftUntilDone, ==, totalSize); \
|
||||
check_uint(tor->info.totalSize, ==, totalSize); \
|
||||
check_uint(tst->haveValid, ==, 0); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
static bool testFileExistsAndConsistsOfThisString(tr_torrent const* tor, tr_file_index_t fileIndex, char const* str)
|
||||
{
|
||||
char* path;
|
||||
size_t const str_len = strlen(str);
|
||||
bool success = false;
|
||||
|
||||
path = tr_torrentFindFile(tor, fileIndex);
|
||||
|
||||
if (path != NULL)
|
||||
{
|
||||
TR_ASSERT(tr_sys_path_exists(path, NULL));
|
||||
|
||||
size_t contents_len;
|
||||
uint8_t* contents = tr_loadFile(path, &contents_len, NULL);
|
||||
|
||||
success = contents != NULL && str_len == contents_len && memcmp(contents, str, contents_len) == 0;
|
||||
|
||||
tr_free(contents);
|
||||
tr_free(path);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void onRenameDone(tr_torrent* tor UNUSED, char const* oldpath UNUSED, char const* newname UNUSED, int error,
|
||||
void* user_data)
|
||||
{
|
||||
*(int*)user_data = error;
|
||||
}
|
||||
|
||||
static int torrentRenameAndWait(tr_torrent* tor, char const* oldpath, char const* newname)
|
||||
{
|
||||
int error = -1;
|
||||
tr_torrentRenamePath(tor, oldpath, newname, onRenameDone, &error);
|
||||
|
||||
do
|
||||
{
|
||||
tr_wait_msec(10);
|
||||
}
|
||||
while (error == -1);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void torrentRemoveAndWait(tr_torrent* tor, int expected_torrent_count)
|
||||
{
|
||||
tr_torrentRemove(tor, false, NULL);
|
||||
|
||||
while (tr_sessionCountTorrents(session) != expected_torrent_count)
|
||||
{
|
||||
tr_wait_msec(10);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static void create_single_file_torrent_contents(char const* top)
|
||||
{
|
||||
char* path = tr_buildPath(top, "hello-world.txt", NULL);
|
||||
libtest_create_file_with_string_contents(path, "hello, world!\n");
|
||||
tr_free(path);
|
||||
}
|
||||
|
||||
static tr_torrent* create_torrent_from_base64_metainfo(tr_ctor* ctor, char const* metainfo_base64)
|
||||
{
|
||||
int err;
|
||||
size_t metainfo_len;
|
||||
char* metainfo;
|
||||
tr_torrent* tor;
|
||||
|
||||
/* create the torrent ctor */
|
||||
metainfo = tr_base64_decode_str(metainfo_base64, &metainfo_len);
|
||||
TR_ASSERT(metainfo != NULL);
|
||||
TR_ASSERT(metainfo_len > 0);
|
||||
tr_ctorSetMetainfo(ctor, (uint8_t*)metainfo, metainfo_len);
|
||||
tr_ctorSetPaused(ctor, TR_FORCE, true);
|
||||
|
||||
/* create the torrent */
|
||||
err = 0;
|
||||
tor = tr_torrentNew(ctor, &err, NULL);
|
||||
TR_ASSERT(err == 0);
|
||||
|
||||
/* cleanup */
|
||||
tr_free(metainfo);
|
||||
return tor;
|
||||
}
|
||||
|
||||
static int test_single_filename_torrent(void)
|
||||
{
|
||||
uint64_t loaded;
|
||||
tr_torrent* tor;
|
||||
char* tmpstr;
|
||||
size_t const totalSize = 14;
|
||||
tr_ctor* ctor;
|
||||
tr_stat const* st;
|
||||
|
||||
/* this is a single-file torrent whose file is hello-world.txt, holding the string "hello, world!" */
|
||||
ctor = tr_ctorNew(session);
|
||||
tor = create_torrent_from_base64_metainfo(ctor,
|
||||
"ZDEwOmNyZWF0ZWQgYnkyNTpUcmFuc21pc3Npb24vMi42MSAoMTM0MDcpMTM6Y3JlYXRpb24gZGF0"
|
||||
"ZWkxMzU4NTQ5MDk4ZTg6ZW5jb2Rpbmc1OlVURi04NDppbmZvZDY6bGVuZ3RoaTE0ZTQ6bmFtZTE1"
|
||||
"OmhlbGxvLXdvcmxkLnR4dDEyOnBpZWNlIGxlbmd0aGkzMjc2OGU2OnBpZWNlczIwOukboJcrkFUY"
|
||||
"f6LvqLXBVvSHqCk6Nzpwcml2YXRlaTBlZWU=");
|
||||
check(tr_isTorrent(tor));
|
||||
|
||||
/* sanity check the info */
|
||||
check_int(tor->info.fileCount, ==, 1);
|
||||
check_str(tor->info.files[0].name, ==, "hello-world.txt");
|
||||
check(!tor->info.files[0].is_renamed);
|
||||
|
||||
/* sanity check the (empty) stats */
|
||||
libttest_blockingTorrentVerify(tor);
|
||||
check_have_none(tor, totalSize);
|
||||
|
||||
create_single_file_torrent_contents(tor->currentDir);
|
||||
|
||||
/* sanity check the stats again, now that we've added the file */
|
||||
libttest_blockingTorrentVerify(tor);
|
||||
st = tr_torrentStat(tor);
|
||||
check_int(st->activity, ==, TR_STATUS_STOPPED);
|
||||
check_int(st->error, ==, TR_STAT_OK);
|
||||
check_uint(st->leftUntilDone, ==, 0);
|
||||
check_uint(st->haveUnchecked, ==, 0);
|
||||
check_uint(st->desiredAvailable, ==, 0);
|
||||
check_uint(st->sizeWhenDone, ==, totalSize);
|
||||
check_uint(st->haveValid, ==, totalSize);
|
||||
|
||||
/**
|
||||
*** okay! we've finally put together all the scaffolding to test
|
||||
*** renaming a single-file torrent
|
||||
**/
|
||||
|
||||
/* confirm that bad inputs get caught */
|
||||
|
||||
check_int(torrentRenameAndWait(tor, "hello-world.txt", NULL), ==, EINVAL);
|
||||
check_int(torrentRenameAndWait(tor, "hello-world.txt", ""), ==, EINVAL);
|
||||
check_int(torrentRenameAndWait(tor, "hello-world.txt", "."), ==, EINVAL);
|
||||
check_int(torrentRenameAndWait(tor, "hello-world.txt", ".."), ==, EINVAL);
|
||||
check_int(torrentRenameAndWait(tor, "hello-world.txt", "hello-world.txt"), ==, 0);
|
||||
check_int(torrentRenameAndWait(tor, "hello-world.txt", "hello/world.txt"), ==, EINVAL);
|
||||
|
||||
check(!tor->info.files[0].is_renamed);
|
||||
check_str(tor->info.files[0].name, ==, "hello-world.txt");
|
||||
|
||||
/***
|
||||
**** Now try a rename that should succeed
|
||||
***/
|
||||
|
||||
tmpstr = tr_buildPath(tor->currentDir, "hello-world.txt", NULL);
|
||||
check(tr_sys_path_exists(tmpstr, NULL));
|
||||
check_str(tr_torrentName(tor), ==, "hello-world.txt");
|
||||
check_int(torrentRenameAndWait(tor, tor->info.name, "foobar"), ==, 0);
|
||||
check(!tr_sys_path_exists(tmpstr, NULL)); /* confirm the old filename can't be found */
|
||||
tr_free(tmpstr);
|
||||
check(tor->info.files[0].is_renamed); /* confirm the file's 'renamed' flag is set */
|
||||
check_str(tr_torrentName(tor), ==, "foobar"); /* confirm the torrent's name is now 'foobar' */
|
||||
check_str(tor->info.files[0].name, ==, "foobar"); /* confirm the file's name is now 'foobar' in our struct */
|
||||
check_str(strstr(tor->info.torrent, "foobar"), ==, NULL); /* confirm the name in the .torrent file hasn't changed */
|
||||
tmpstr = tr_buildPath(tor->currentDir, "foobar", NULL);
|
||||
check(tr_sys_path_exists(tmpstr, NULL)); /* confirm the file's name is now 'foobar' on the disk */
|
||||
tr_free(tmpstr);
|
||||
check(testFileExistsAndConsistsOfThisString(tor, 0, "hello, world!\n")); /* confirm the contents are right */
|
||||
|
||||
/* (while it's renamed: confirm that the .resume file remembers the changes) */
|
||||
tr_torrentSaveResume(tor);
|
||||
libttest_sync();
|
||||
loaded = tr_torrentLoadResume(tor, ~0, ctor, NULL);
|
||||
check_str(tr_torrentName(tor), ==, "foobar");
|
||||
check_uint((loaded & TR_FR_NAME), !=, 0);
|
||||
|
||||
/***
|
||||
**** ...and rename it back again
|
||||
***/
|
||||
|
||||
tmpstr = tr_buildPath(tor->currentDir, "foobar", NULL);
|
||||
check(tr_sys_path_exists(tmpstr, NULL));
|
||||
check_int(torrentRenameAndWait(tor, "foobar", "hello-world.txt"), ==, 0);
|
||||
check(!tr_sys_path_exists(tmpstr, NULL));
|
||||
check(tor->info.files[0].is_renamed);
|
||||
check_str(tor->info.files[0].name, ==, "hello-world.txt");
|
||||
check_str(tr_torrentName(tor), ==, "hello-world.txt");
|
||||
tr_free(tmpstr);
|
||||
check(testFileExistsAndConsistsOfThisString(tor, 0, "hello, world!\n"));
|
||||
|
||||
/* cleanup */
|
||||
tr_ctorFree(ctor);
|
||||
torrentRemoveAndWait(tor, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
****
|
||||
****
|
||||
***/
|
||||
|
||||
static void create_multifile_torrent_contents(char const* top)
|
||||
{
|
||||
char* path;
|
||||
|
||||
path = tr_buildPath(top, "Felidae", "Felinae", "Acinonyx", "Cheetah", "Chester", NULL);
|
||||
libtest_create_file_with_string_contents(path, "It ain't easy bein' cheesy.\n");
|
||||
tr_free(path);
|
||||
|
||||
path = tr_buildPath(top, "Felidae", "Pantherinae", "Panthera", "Tiger", "Tony", NULL);
|
||||
libtest_create_file_with_string_contents(path, "They’re Grrrrreat!\n");
|
||||
tr_free(path);
|
||||
|
||||
path = tr_buildPath(top, "Felidae", "Felinae", "Felis", "catus", "Kyphi", NULL);
|
||||
libtest_create_file_with_string_contents(path, "Inquisitive\n");
|
||||
tr_free(path);
|
||||
|
||||
path = tr_buildPath(top, "Felidae", "Felinae", "Felis", "catus", "Saffron", NULL);
|
||||
libtest_create_file_with_string_contents(path, "Tough\n");
|
||||
tr_free(path);
|
||||
|
||||
libttest_sync();
|
||||
}
|
||||
|
||||
static int test_multifile_torrent(void)
|
||||
{
|
||||
uint64_t loaded;
|
||||
tr_torrent* tor;
|
||||
tr_ctor* ctor;
|
||||
char* str;
|
||||
char* tmp;
|
||||
static size_t const totalSize = 67;
|
||||
tr_stat const* st;
|
||||
tr_file const* files;
|
||||
char const* strings[4];
|
||||
char const* expected_files[4] =
|
||||
{
|
||||
"Felidae/Felinae/Acinonyx/Cheetah/Chester",
|
||||
"Felidae/Felinae/Felis/catus/Kyphi",
|
||||
"Felidae/Felinae/Felis/catus/Saffron",
|
||||
"Felidae/Pantherinae/Panthera/Tiger/Tony"
|
||||
};
|
||||
char const* expected_contents[4] =
|
||||
{
|
||||
"It ain't easy bein' cheesy.\n",
|
||||
"Inquisitive\n",
|
||||
"Tough\n",
|
||||
"They’re Grrrrreat!\n"
|
||||
};
|
||||
|
||||
ctor = tr_ctorNew(session);
|
||||
tor = create_torrent_from_base64_metainfo(ctor,
|
||||
"ZDEwOmNyZWF0ZWQgYnkyNTpUcmFuc21pc3Npb24vMi42MSAoMTM0MDcpMTM6Y3JlYXRpb24gZGF0"
|
||||
"ZWkxMzU4NTU1NDIwZTg6ZW5jb2Rpbmc1OlVURi04NDppbmZvZDU6ZmlsZXNsZDY6bGVuZ3RoaTI4"
|
||||
"ZTQ6cGF0aGw3OkZlbGluYWU4OkFjaW5vbnl4NzpDaGVldGFoNzpDaGVzdGVyZWVkNjpsZW5ndGhp"
|
||||
"MTJlNDpwYXRobDc6RmVsaW5hZTU6RmVsaXM1OmNhdHVzNTpLeXBoaWVlZDY6bGVuZ3RoaTZlNDpw"
|
||||
"YXRobDc6RmVsaW5hZTU6RmVsaXM1OmNhdHVzNzpTYWZmcm9uZWVkNjpsZW5ndGhpMjFlNDpwYXRo"
|
||||
"bDExOlBhbnRoZXJpbmFlODpQYW50aGVyYTU6VGlnZXI0OlRvbnllZWU0Om5hbWU3OkZlbGlkYWUx"
|
||||
"MjpwaWVjZSBsZW5ndGhpMzI3NjhlNjpwaWVjZXMyMDp27buFkmy8ICfNX4nsJmt0Ckm2Ljc6cHJp"
|
||||
"dmF0ZWkwZWVl");
|
||||
check(tr_isTorrent(tor));
|
||||
files = tor->info.files;
|
||||
|
||||
/* sanity check the info */
|
||||
check_str(tor->info.name, ==, "Felidae");
|
||||
check_uint(tor->info.totalSize, ==, totalSize);
|
||||
check_uint(tor->info.fileCount, ==, 4);
|
||||
|
||||
for (tr_file_index_t i = 0; i < 4; ++i)
|
||||
{
|
||||
check_str(files[i].name, ==, expected_files[i]);
|
||||
}
|
||||
|
||||
/* sanity check the (empty) stats */
|
||||
libttest_blockingTorrentVerify(tor);
|
||||
check_have_none(tor, totalSize);
|
||||
|
||||
/* build the local data */
|
||||
create_multifile_torrent_contents(tor->currentDir);
|
||||
|
||||
/* sanity check the (full) stats */
|
||||
libttest_blockingTorrentVerify(tor);
|
||||
st = tr_torrentStat(tor);
|
||||
check_int(st->activity, ==, TR_STATUS_STOPPED);
|
||||
check_int(st->error, ==, TR_STAT_OK);
|
||||
check_uint(st->leftUntilDone, ==, 0);
|
||||
check_uint(st->haveUnchecked, ==, 0);
|
||||
check_uint(st->desiredAvailable, ==, 0);
|
||||
check_uint(st->sizeWhenDone, ==, totalSize);
|
||||
check_uint(st->haveValid, ==, totalSize);
|
||||
|
||||
/**
|
||||
*** okay! let's test renaming.
|
||||
**/
|
||||
|
||||
/* rename a leaf... */
|
||||
check_int(torrentRenameAndWait(tor, "Felidae/Felinae/Felis/catus/Kyphi", "placeholder"), ==, 0);
|
||||
check_str(files[1].name, ==, "Felidae/Felinae/Felis/catus/placeholder");
|
||||
check(testFileExistsAndConsistsOfThisString(tor, 1, "Inquisitive\n"));
|
||||
|
||||
/* ...and back again */
|
||||
check_int(torrentRenameAndWait(tor, "Felidae/Felinae/Felis/catus/placeholder", "Kyphi"), ==, 0);
|
||||
check_str(files[1].name, ==, "Felidae/Felinae/Felis/catus/Kyphi");
|
||||
testFileExistsAndConsistsOfThisString(tor, 1, "Inquisitive\n");
|
||||
|
||||
/* rename a branch... */
|
||||
check_int(torrentRenameAndWait(tor, "Felidae/Felinae/Felis/catus", "placeholder"), ==, 0);
|
||||
check_str(files[0].name, ==, expected_files[0]);
|
||||
check_str(files[1].name, ==, "Felidae/Felinae/Felis/placeholder/Kyphi");
|
||||
check_str(files[2].name, ==, "Felidae/Felinae/Felis/placeholder/Saffron");
|
||||
check_str(files[3].name, ==, expected_files[3]);
|
||||
check(testFileExistsAndConsistsOfThisString(tor, 1, expected_contents[1]));
|
||||
check(testFileExistsAndConsistsOfThisString(tor, 2, expected_contents[2]));
|
||||
check(!files[0].is_renamed);
|
||||
check(files[1].is_renamed);
|
||||
check(files[2].is_renamed);
|
||||
check(!files[3].is_renamed);
|
||||
|
||||
/* (while the branch is renamed: confirm that the .resume file remembers the changes) */
|
||||
tr_torrentSaveResume(tor);
|
||||
/* this is a bit dodgy code-wise, but let's make sure the .resume file got the name */
|
||||
tr_free(files[1].name);
|
||||
tor->info.files[1].name = tr_strdup("gabba gabba hey");
|
||||
loaded = tr_torrentLoadResume(tor, ~0, ctor, NULL);
|
||||
check_uint((loaded & TR_FR_FILENAMES), !=, 0);
|
||||
check_str(files[0].name, ==, expected_files[0]);
|
||||
check_str(files[1].name, ==, "Felidae/Felinae/Felis/placeholder/Kyphi");
|
||||
check_str(files[2].name, ==, "Felidae/Felinae/Felis/placeholder/Saffron");
|
||||
check_str(files[3].name, ==, expected_files[3]);
|
||||
|
||||
/* ...and back again */
|
||||
check_int(torrentRenameAndWait(tor, "Felidae/Felinae/Felis/placeholder", "catus"), ==, 0);
|
||||
|
||||
for (tr_file_index_t i = 0; i < 4; ++i)
|
||||
{
|
||||
check_str(files[i].name, ==, expected_files[i]);
|
||||
check(testFileExistsAndConsistsOfThisString(tor, i, expected_contents[i]));
|
||||
}
|
||||
|
||||
check(!files[0].is_renamed);
|
||||
check(files[1].is_renamed);
|
||||
check(files[2].is_renamed);
|
||||
check(!files[3].is_renamed);
|
||||
|
||||
/***
|
||||
**** Test it an incomplete torrent...
|
||||
***/
|
||||
|
||||
/* remove the directory Felidae/Felinae/Felis/catus */
|
||||
str = tr_torrentFindFile(tor, 1);
|
||||
check_str(str, !=, NULL);
|
||||
tr_sys_path_remove(str, NULL);
|
||||
tr_free(str);
|
||||
str = tr_torrentFindFile(tor, 2);
|
||||
check_str(str, !=, NULL);
|
||||
tr_sys_path_remove(str, NULL);
|
||||
tmp = tr_sys_path_dirname(str, NULL);
|
||||
tr_sys_path_remove(tmp, NULL);
|
||||
tr_free(tmp);
|
||||
tr_free(str);
|
||||
libttest_sync();
|
||||
libttest_blockingTorrentVerify(tor);
|
||||
testFileExistsAndConsistsOfThisString(tor, 0, expected_contents[0]);
|
||||
|
||||
for (tr_file_index_t i = 1; i <= 2; ++i)
|
||||
{
|
||||
str = tr_torrentFindFile(tor, i);
|
||||
check_str(str, ==, NULL);
|
||||
tr_free(str);
|
||||
}
|
||||
|
||||
testFileExistsAndConsistsOfThisString(tor, 3, expected_contents[3]);
|
||||
|
||||
/* rename a branch... */
|
||||
check_int(torrentRenameAndWait(tor, "Felidae/Felinae/Felis/catus", "foo"), ==, 0);
|
||||
check_str(files[0].name, ==, expected_files[0]);
|
||||
check_str(files[1].name, ==, "Felidae/Felinae/Felis/foo/Kyphi");
|
||||
check_str(files[2].name, ==, "Felidae/Felinae/Felis/foo/Saffron");
|
||||
check_str(files[3].name, ==, expected_files[3]);
|
||||
|
||||
/* ...and back again */
|
||||
check_int(torrentRenameAndWait(tor, "Felidae/Felinae/Felis/foo", "catus"), ==, 0);
|
||||
|
||||
for (tr_file_index_t i = 0; i < 4; ++i)
|
||||
{
|
||||
check_str(files[i].name, ==, expected_files[i]);
|
||||
}
|
||||
|
||||
check_int(torrentRenameAndWait(tor, "Felidae", "gabba"), ==, 0);
|
||||
strings[0] = "gabba/Felinae/Acinonyx/Cheetah/Chester";
|
||||
strings[1] = "gabba/Felinae/Felis/catus/Kyphi";
|
||||
strings[2] = "gabba/Felinae/Felis/catus/Saffron";
|
||||
strings[3] = "gabba/Pantherinae/Panthera/Tiger/Tony";
|
||||
|
||||
for (tr_file_index_t i = 0; i < 4; ++i)
|
||||
{
|
||||
check_str(files[i].name, ==, strings[i]);
|
||||
testFileExistsAndConsistsOfThisString(tor, i, expected_contents[i]);
|
||||
}
|
||||
|
||||
/* rename the root, then a branch, and then a leaf... */
|
||||
check_int(torrentRenameAndWait(tor, "gabba", "Felidae"), ==, 0);
|
||||
check_int(torrentRenameAndWait(tor, "Felidae/Pantherinae/Panthera/Tiger", "Snow Leopard"), ==, 0);
|
||||
check_int(torrentRenameAndWait(tor, "Felidae/Pantherinae/Panthera/Snow Leopard/Tony", "10.6"), ==, 0);
|
||||
strings[0] = "Felidae/Felinae/Acinonyx/Cheetah/Chester";
|
||||
strings[1] = "Felidae/Felinae/Felis/catus/Kyphi";
|
||||
strings[2] = "Felidae/Felinae/Felis/catus/Saffron";
|
||||
strings[3] = "Felidae/Pantherinae/Panthera/Snow Leopard/10.6";
|
||||
|
||||
for (tr_file_index_t i = 0; i < 4; ++i)
|
||||
{
|
||||
check_str(files[i].name, ==, strings[i]);
|
||||
testFileExistsAndConsistsOfThisString(tor, i, expected_contents[i]);
|
||||
}
|
||||
|
||||
tr_ctorFree(ctor);
|
||||
torrentRemoveAndWait(tor, 0);
|
||||
|
||||
/**
|
||||
*** Test renaming prefixes (shouldn't work)
|
||||
**/
|
||||
|
||||
ctor = tr_ctorNew(session);
|
||||
tor = create_torrent_from_base64_metainfo(ctor,
|
||||
"ZDEwOmNyZWF0ZWQgYnkyNTpUcmFuc21pc3Npb24vMi42MSAoMTM0MDcpMTM6Y3JlYXRpb24gZGF0"
|
||||
"ZWkxMzU4NTU1NDIwZTg6ZW5jb2Rpbmc1OlVURi04NDppbmZvZDU6ZmlsZXNsZDY6bGVuZ3RoaTI4"
|
||||
"ZTQ6cGF0aGw3OkZlbGluYWU4OkFjaW5vbnl4NzpDaGVldGFoNzpDaGVzdGVyZWVkNjpsZW5ndGhp"
|
||||
"MTJlNDpwYXRobDc6RmVsaW5hZTU6RmVsaXM1OmNhdHVzNTpLeXBoaWVlZDY6bGVuZ3RoaTZlNDpw"
|
||||
"YXRobDc6RmVsaW5hZTU6RmVsaXM1OmNhdHVzNzpTYWZmcm9uZWVkNjpsZW5ndGhpMjFlNDpwYXRo"
|
||||
"bDExOlBhbnRoZXJpbmFlODpQYW50aGVyYTU6VGlnZXI0OlRvbnllZWU0Om5hbWU3OkZlbGlkYWUx"
|
||||
"MjpwaWVjZSBsZW5ndGhpMzI3NjhlNjpwaWVjZXMyMDp27buFkmy8ICfNX4nsJmt0Ckm2Ljc6cHJp"
|
||||
"dmF0ZWkwZWVl");
|
||||
check(tr_isTorrent(tor));
|
||||
files = tor->info.files;
|
||||
|
||||
/* rename prefix of top */
|
||||
check_int(torrentRenameAndWait(tor, "Feli", "FelidaeX"), ==, EINVAL);
|
||||
check_str(tor->info.name, ==, "Felidae");
|
||||
check(!files[0].is_renamed);
|
||||
check(!files[1].is_renamed);
|
||||
check(!files[2].is_renamed);
|
||||
check(!files[3].is_renamed);
|
||||
|
||||
/* rename false path */
|
||||
check_int(torrentRenameAndWait(tor, "Felidae/FelinaeX", "Genus Felinae"), ==, EINVAL);
|
||||
check_str(tor->info.name, ==, "Felidae");
|
||||
check(!files[0].is_renamed);
|
||||
check(!files[1].is_renamed);
|
||||
check(!files[2].is_renamed);
|
||||
check(!files[3].is_renamed);
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
/* cleanup */
|
||||
tr_ctorFree(ctor);
|
||||
torrentRemoveAndWait(tor, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static int test_partial_file(void)
|
||||
{
|
||||
tr_torrent* tor;
|
||||
tr_stat const* st;
|
||||
tr_file_stat* fst;
|
||||
uint32_t const pieceCount = 33;
|
||||
uint32_t const pieceSize = 32768;
|
||||
uint32_t const length[] = { 1048576, 4096, 512 };
|
||||
uint64_t const totalSize = length[0] + length[1] + length[2];
|
||||
char const* strings[3];
|
||||
|
||||
/***
|
||||
**** create our test torrent with an incomplete .part file
|
||||
***/
|
||||
|
||||
tor = libttest_zero_torrent_init(session);
|
||||
check_uint(tor->info.totalSize, ==, totalSize);
|
||||
check_uint(tor->info.pieceSize, ==, pieceSize);
|
||||
check_uint(tor->info.pieceCount, ==, pieceCount);
|
||||
check_str(tor->info.files[0].name, ==, "files-filled-with-zeroes/1048576");
|
||||
check_str(tor->info.files[1].name, ==, "files-filled-with-zeroes/4096");
|
||||
check_str(tor->info.files[2].name, ==, "files-filled-with-zeroes/512");
|
||||
|
||||
libttest_zero_torrent_populate(tor, false);
|
||||
fst = tr_torrentFiles(tor, NULL);
|
||||
check_uint(fst[0].bytesCompleted, ==, length[0] - pieceSize);
|
||||
check_uint(fst[1].bytesCompleted, ==, length[1]);
|
||||
check_uint(fst[2].bytesCompleted, ==, length[2]);
|
||||
tr_torrentFilesFree(fst, tor->info.fileCount);
|
||||
st = tr_torrentStat(tor);
|
||||
check_uint(st->sizeWhenDone, ==, totalSize);
|
||||
check_uint(st->leftUntilDone, ==, pieceSize);
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
check_int(torrentRenameAndWait(tor, "files-filled-with-zeroes", "foo"), ==, 0);
|
||||
check_int(torrentRenameAndWait(tor, "foo/1048576", "bar"), ==, 0);
|
||||
strings[0] = "foo/bar";
|
||||
strings[1] = "foo/4096";
|
||||
strings[2] = "foo/512";
|
||||
|
||||
for (tr_file_index_t i = 0; i < 3; ++i)
|
||||
{
|
||||
check_str(tor->info.files[i].name, ==, strings[i]);
|
||||
}
|
||||
|
||||
strings[0] = "foo/bar.part";
|
||||
|
||||
for (tr_file_index_t i = 0; i < 3; ++i)
|
||||
{
|
||||
char* expected = tr_buildPath(tor->currentDir, strings[i], NULL);
|
||||
char* path = tr_torrentFindFile(tor, i);
|
||||
check_str(path, ==, expected);
|
||||
tr_free(path);
|
||||
tr_free(expected);
|
||||
}
|
||||
|
||||
torrentRemoveAndWait(tor, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testFunc const tests[] =
|
||||
{
|
||||
test_single_filename_torrent,
|
||||
test_multifile_torrent,
|
||||
test_partial_file
|
||||
};
|
||||
|
||||
session = libttest_session_init(NULL);
|
||||
|
||||
int ret = runTests(tests, NUM_TESTS(tests));
|
||||
|
||||
libttest_session_close(session);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -593,7 +593,6 @@ static uint64_t loadProgress(tr_variant* dict, tr_torrent* tor)
|
|||
uint8_t const* raw;
|
||||
size_t rawlen;
|
||||
tr_variant* l;
|
||||
tr_variant* b;
|
||||
struct tr_bitfield blocks = TR_BITFIELD_INIT;
|
||||
|
||||
if (tr_variantDictFindList(prog, TR_KEY_time_checked, &l))
|
||||
|
@ -667,6 +666,7 @@ static uint64_t loadProgress(tr_variant* dict, tr_torrent* tor)
|
|||
err = NULL;
|
||||
tr_bitfieldConstruct(&blocks, tor->blockCount);
|
||||
|
||||
tr_variant* b;
|
||||
if ((b = tr_variantDictFind(prog, TR_KEY_blocks)) != NULL)
|
||||
{
|
||||
size_t buflen;
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
enum
|
||||
{
|
||||
TR_FR_DOWNLOADED = (1 << 0),
|
||||
|
@ -49,3 +53,5 @@ void tr_torrentSaveResume(tr_torrent* tor);
|
|||
void tr_torrentRemoveResume(tr_torrent const* tor);
|
||||
|
||||
int tr_torrentRenameResume(tr_torrent const* tor, char const* newname);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,166 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "transmission.h"
|
||||
#include "rpcimpl.h"
|
||||
#include "utils.h"
|
||||
#include "variant.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
static int test_list(void)
|
||||
{
|
||||
size_t len;
|
||||
int64_t i;
|
||||
char const* str;
|
||||
tr_variant top;
|
||||
|
||||
tr_rpc_parse_list_str(&top, "12", TR_BAD_SIZE);
|
||||
check(tr_variantIsInt(&top));
|
||||
check(tr_variantGetInt(&top, &i));
|
||||
check_int(i, ==, 12);
|
||||
tr_variantFree(&top);
|
||||
|
||||
tr_rpc_parse_list_str(&top, "12", 1);
|
||||
check(tr_variantIsInt(&top));
|
||||
check(tr_variantGetInt(&top, &i));
|
||||
check_int(i, ==, 1);
|
||||
tr_variantFree(&top);
|
||||
|
||||
tr_rpc_parse_list_str(&top, "6,7", TR_BAD_SIZE);
|
||||
check(tr_variantIsList(&top));
|
||||
check_uint(tr_variantListSize(&top), ==, 2);
|
||||
check(tr_variantGetInt(tr_variantListChild(&top, 0), &i));
|
||||
check_int(i, ==, 6);
|
||||
check(tr_variantGetInt(tr_variantListChild(&top, 1), &i));
|
||||
check_int(i, ==, 7);
|
||||
tr_variantFree(&top);
|
||||
|
||||
tr_rpc_parse_list_str(&top, "asdf", TR_BAD_SIZE);
|
||||
check(tr_variantIsString(&top));
|
||||
check(tr_variantGetStr(&top, &str, &len));
|
||||
check_uint(len, ==, 4);
|
||||
check_str(str, ==, "asdf");
|
||||
tr_variantFree(&top);
|
||||
|
||||
tr_rpc_parse_list_str(&top, "1,3-5", TR_BAD_SIZE);
|
||||
check(tr_variantIsList(&top));
|
||||
check_uint(tr_variantListSize(&top), ==, 4);
|
||||
check(tr_variantGetInt(tr_variantListChild(&top, 0), &i));
|
||||
check_int(i, ==, 1);
|
||||
check(tr_variantGetInt(tr_variantListChild(&top, 1), &i));
|
||||
check_int(i, ==, 3);
|
||||
check(tr_variantGetInt(tr_variantListChild(&top, 2), &i));
|
||||
check_int(i, ==, 4);
|
||||
check(tr_variantGetInt(tr_variantListChild(&top, 3), &i));
|
||||
check_int(i, ==, 5);
|
||||
tr_variantFree(&top);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static void rpc_response_func(tr_session* session UNUSED, tr_variant* response, void* setme)
|
||||
{
|
||||
*(tr_variant*)setme = *response;
|
||||
tr_variantInitBool(response, false);
|
||||
}
|
||||
|
||||
static int test_session_get_and_set(void)
|
||||
{
|
||||
tr_session* session;
|
||||
tr_variant request;
|
||||
tr_variant response;
|
||||
tr_variant* args;
|
||||
tr_torrent* tor;
|
||||
|
||||
session = libttest_session_init(NULL);
|
||||
tor = libttest_zero_torrent_init(session);
|
||||
check_ptr(tor, !=, NULL);
|
||||
|
||||
tr_variantInitDict(&request, 1);
|
||||
tr_variantDictAddStr(&request, TR_KEY_method, "session-get");
|
||||
tr_rpc_request_exec_json(session, &request, rpc_response_func, &response);
|
||||
tr_variantFree(&request);
|
||||
|
||||
check(tr_variantIsDict(&response));
|
||||
check(tr_variantDictFindDict(&response, TR_KEY_arguments, &args));
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_alt_speed_down), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_alt_speed_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_alt_speed_time_begin), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_alt_speed_time_day), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_alt_speed_time_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_alt_speed_time_end), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_alt_speed_up), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_blocklist_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_blocklist_size), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_blocklist_url), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_cache_size_mb), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_config_dir), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_dht_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_download_dir), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_download_dir_free_space), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_download_queue_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_download_queue_size), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_encryption), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_idle_seeding_limit), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_idle_seeding_limit_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_incomplete_dir), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_incomplete_dir_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_lpd_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_peer_limit_global), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_peer_limit_per_torrent), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_peer_port), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_peer_port_random_on_start), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_pex_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_port_forwarding_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_queue_stalled_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_queue_stalled_minutes), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_rename_partial_files), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_rpc_version), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_rpc_version_minimum), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_script_torrent_done_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_script_torrent_done_filename), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_seed_queue_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_seed_queue_size), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_seedRatioLimit), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_seedRatioLimited), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_speed_limit_down), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_speed_limit_down_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_speed_limit_up), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_speed_limit_up_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_start_added_torrents), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_trash_original_torrent_files), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_units), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_utp_enabled), !=, NULL);
|
||||
check_ptr(tr_variantDictFind(args, TR_KEY_version), !=, NULL);
|
||||
tr_variantFree(&response);
|
||||
|
||||
/* cleanup */
|
||||
tr_torrentRemove(tor, false, NULL);
|
||||
libttest_session_close(session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testFunc const tests[] =
|
||||
{
|
||||
test_list,
|
||||
test_session_get_and_set
|
||||
};
|
||||
|
||||
return runTests(tests, NUM_TESTS(tests));
|
||||
}
|
|
@ -121,7 +121,6 @@ static tr_torrent** getTorrents(tr_session* session, tr_variant* args, int* setm
|
|||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
char const* str;
|
||||
tr_torrent* tor;
|
||||
tr_variant* node = tr_variantListChild(ids, i);
|
||||
|
||||
|
@ -730,9 +729,9 @@ static void initField(tr_torrent* const tor, tr_info const* const inf, tr_stat c
|
|||
{
|
||||
size_t byte_count = 0;
|
||||
void* bytes = tr_torrentCreatePieceBitfield(tor, &byte_count);
|
||||
char* str = tr_base64_encode(bytes, byte_count, NULL);
|
||||
tr_variantInitStr(initme, str != NULL ? str : "", TR_BAD_SIZE);
|
||||
tr_free(str);
|
||||
char* enc = tr_base64_encode(bytes, byte_count, NULL);
|
||||
tr_variantInitStr(initme, enc != NULL ? enc : "", TR_BAD_SIZE);
|
||||
tr_free(enc);
|
||||
tr_free(bytes);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -8,14 +8,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "transmission.h"
|
||||
#include "tr-macros.h"
|
||||
#include "variant.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
/***
|
||||
**** RPC processing
|
||||
***/
|
||||
|
@ -32,6 +30,4 @@ void tr_rpc_request_exec_uri(tr_session* session, void const* request_uri, size_
|
|||
|
||||
void tr_rpc_parse_list_str(tr_variant* setme, char const* list_str, size_t list_str_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -8,10 +8,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
typedef struct tr_session_id* tr_session_id_t;
|
||||
|
||||
|
@ -52,6 +51,4 @@ char const* tr_session_id_get_current(tr_session_id_t session_id);
|
|||
*/
|
||||
bool tr_session_id_is_local(char const* session_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "transmission.h"
|
||||
#include "session.h"
|
||||
#include "session-id.h"
|
||||
#include "utils.h"
|
||||
#include "version.h"
|
||||
|
||||
#undef VERBOSE
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
static int testPeerId(void)
|
||||
{
|
||||
uint8_t peer_id[PEER_ID_LEN + 1];
|
||||
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
tr_peerIdInit(peer_id);
|
||||
|
||||
check_uint(strlen((char*)peer_id), ==, PEER_ID_LEN);
|
||||
check_mem(peer_id, ==, PEERID_PREFIX, 8);
|
||||
|
||||
for (int j = 8; j < PEER_ID_LEN; ++j)
|
||||
{
|
||||
char tmp[2] = { (char)peer_id[j], '\0' };
|
||||
val += strtoul(tmp, NULL, 36);
|
||||
}
|
||||
|
||||
check_int(val % 36, ==, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_session_id(void)
|
||||
{
|
||||
tr_session_id_t session_id;
|
||||
char const* session_id_str_1 = NULL;
|
||||
char const* session_id_str_2 = NULL;
|
||||
char const* session_id_str_3 = NULL;
|
||||
|
||||
check(!tr_session_id_is_local(NULL));
|
||||
check(!tr_session_id_is_local(""));
|
||||
check(!tr_session_id_is_local("test"));
|
||||
|
||||
session_id = tr_session_id_new();
|
||||
check_ptr(session_id, !=, NULL);
|
||||
|
||||
tr_timeUpdate(0);
|
||||
|
||||
session_id_str_1 = tr_session_id_get_current(session_id);
|
||||
check_str(session_id_str_1, !=, NULL);
|
||||
check_uint(strlen(session_id_str_1), ==, 48);
|
||||
session_id_str_1 = tr_strdup(session_id_str_1);
|
||||
|
||||
check(tr_session_id_is_local(session_id_str_1));
|
||||
|
||||
tr_timeUpdate(60 * 60 - 1);
|
||||
|
||||
check(tr_session_id_is_local(session_id_str_1));
|
||||
|
||||
session_id_str_2 = tr_session_id_get_current(session_id);
|
||||
check_str(session_id_str_2, !=, NULL);
|
||||
check_uint(strlen(session_id_str_2), ==, 48);
|
||||
check_str(session_id_str_2, ==, session_id_str_1);
|
||||
|
||||
tr_timeUpdate(60 * 60);
|
||||
|
||||
check(tr_session_id_is_local(session_id_str_1));
|
||||
|
||||
session_id_str_2 = tr_session_id_get_current(session_id);
|
||||
check_str(session_id_str_2, !=, NULL);
|
||||
check_uint(strlen(session_id_str_2), ==, 48);
|
||||
check_str(session_id_str_2, !=, session_id_str_1);
|
||||
session_id_str_2 = tr_strdup(session_id_str_2);
|
||||
|
||||
check(tr_session_id_is_local(session_id_str_2));
|
||||
check(tr_session_id_is_local(session_id_str_1));
|
||||
|
||||
tr_timeUpdate(60 * 60 * 2);
|
||||
|
||||
check(tr_session_id_is_local(session_id_str_2));
|
||||
check(tr_session_id_is_local(session_id_str_1));
|
||||
|
||||
session_id_str_3 = tr_session_id_get_current(session_id);
|
||||
check_str(session_id_str_3, !=, NULL);
|
||||
check_uint(strlen(session_id_str_3), ==, 48);
|
||||
check_str(session_id_str_3, !=, session_id_str_2);
|
||||
check_str(session_id_str_3, !=, session_id_str_1);
|
||||
session_id_str_3 = tr_strdup(session_id_str_3);
|
||||
|
||||
check(tr_session_id_is_local(session_id_str_3));
|
||||
check(tr_session_id_is_local(session_id_str_2));
|
||||
check(!tr_session_id_is_local(session_id_str_1));
|
||||
|
||||
tr_timeUpdate(60 * 60 * 10);
|
||||
|
||||
check(tr_session_id_is_local(session_id_str_3));
|
||||
check(tr_session_id_is_local(session_id_str_2));
|
||||
check(!tr_session_id_is_local(session_id_str_1));
|
||||
|
||||
check(!tr_session_id_is_local(NULL));
|
||||
check(!tr_session_id_is_local(""));
|
||||
check(!tr_session_id_is_local("test"));
|
||||
|
||||
tr_session_id_free(session_id);
|
||||
|
||||
check(!tr_session_id_is_local(session_id_str_3));
|
||||
check(!tr_session_id_is_local(session_id_str_2));
|
||||
check(!tr_session_id_is_local(session_id_str_1));
|
||||
|
||||
tr_free((char*)session_id_str_3);
|
||||
tr_free((char*)session_id_str_2);
|
||||
tr_free((char*)session_id_str_1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testFunc const tests[] =
|
||||
{
|
||||
testPeerId,
|
||||
test_session_id
|
||||
};
|
||||
|
||||
return runTests(tests, NUM_TESTS(tests));
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
#include <event2/dns.h> /* evdns_base_free() */
|
||||
#include <event2/event.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <libutp/utp.h>
|
||||
|
||||
// #define TR_SHOW_DEPRECATED
|
||||
|
@ -1489,7 +1490,7 @@ static void turtleUpdateTable(struct tr_turtle_info* t)
|
|||
end += MINUTES_PER_DAY;
|
||||
}
|
||||
|
||||
for (int i = begin; i < end; ++i)
|
||||
for (time_t i = begin; i < end; ++i)
|
||||
{
|
||||
tr_bitfieldAdd(b, (i + day * MINUTES_PER_DAY) % MINUTES_PER_WEEK);
|
||||
}
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
#include "bandwidth.h"
|
||||
#include "bitfield.h"
|
||||
#include "net.h"
|
||||
#include "tr-macros.h"
|
||||
#include "utils.h"
|
||||
#include "variant.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_NET_OK,
|
||||
|
@ -320,3 +323,5 @@ bool tr_sessionGetActiveSpeedLimit_Bps(tr_session const* session, tr_direction d
|
|||
void tr_sessionGetNextQueuedTorrents(tr_session* session, tr_direction dir, size_t numwanted, tr_ptrArray* setme);
|
||||
|
||||
int tr_sessionCountQueueFreeSlots(tr_session* session, tr_direction);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,401 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2017 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "transmission.h"
|
||||
#include "error.h"
|
||||
#include "file.h"
|
||||
#include "subprocess.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
static char arg_dump_args[] = "--dump-args";
|
||||
static char arg_dump_env[] = "--dump-env";
|
||||
static char arg_dump_cwd[] = "--dump-cwd";
|
||||
|
||||
static char* self_path = NULL;
|
||||
|
||||
static int test_spawn_async_missing_exe(void)
|
||||
{
|
||||
char missing_exe_path[] = TR_IF_WIN32("C:\\", "/") "tr-missing-test-exe" TR_IF_WIN32(".exe", "");
|
||||
|
||||
char* const args[] =
|
||||
{
|
||||
missing_exe_path,
|
||||
NULL
|
||||
};
|
||||
|
||||
tr_error* error = NULL;
|
||||
bool const ret = tr_spawn_async(args, NULL, NULL, &error);
|
||||
check_bool(ret, ==, false);
|
||||
check_ptr(error, !=, NULL);
|
||||
check_int(error->code, !=, 0);
|
||||
check_str(error->message, !=, NULL);
|
||||
|
||||
tr_error_clear(&error);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_spawn_async_args(void)
|
||||
{
|
||||
char* const test_dir = libtest_sandbox_create();
|
||||
char* const result_path = tr_sys_path_native_separators(tr_buildPath(test_dir, "result.txt", NULL));
|
||||
bool const allow_batch_metachars = TR_IF_WIN32(false, true) || !tr_str_has_suffix(self_path, ".cmd");
|
||||
|
||||
char test_arg_1[] = "arg1 ";
|
||||
char test_arg_2[] = " arg2";
|
||||
char test_arg_3[] = "";
|
||||
char test_arg_4[] = "\"arg3'^! $PATH %PATH% \\";
|
||||
|
||||
char* const args[] =
|
||||
{
|
||||
self_path,
|
||||
result_path,
|
||||
arg_dump_args,
|
||||
test_arg_1,
|
||||
test_arg_2,
|
||||
test_arg_3,
|
||||
allow_batch_metachars ? test_arg_4 : NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
tr_error* error = NULL;
|
||||
bool const ret = tr_spawn_async(args, NULL, NULL, &error);
|
||||
check_bool(ret, ==, true);
|
||||
check_ptr(error, ==, NULL);
|
||||
|
||||
while (!tr_sys_path_exists(result_path, NULL))
|
||||
{
|
||||
tr_wait_msec(10);
|
||||
}
|
||||
|
||||
tr_sys_file_t fd = tr_sys_file_open(result_path, TR_SYS_FILE_READ, 0, NULL);
|
||||
check_int(fd, !=, TR_BAD_SYS_FILE);
|
||||
|
||||
char buffer[1024];
|
||||
|
||||
check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
check_str(buffer, ==, test_arg_1);
|
||||
|
||||
check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
check_str(buffer, ==, test_arg_2);
|
||||
|
||||
check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
check_str(buffer, ==, test_arg_3);
|
||||
|
||||
if (allow_batch_metachars)
|
||||
{
|
||||
check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
check_str(buffer, ==, test_arg_4);
|
||||
}
|
||||
|
||||
check(!tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
|
||||
tr_sys_file_close(fd, NULL);
|
||||
|
||||
tr_free(result_path);
|
||||
libtest_sandbox_destroy(test_dir);
|
||||
tr_free(test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_spawn_async_env(void)
|
||||
{
|
||||
char* const test_dir = libtest_sandbox_create();
|
||||
char* const result_path = tr_sys_path_native_separators(tr_buildPath(test_dir, "result.txt", NULL));
|
||||
|
||||
char test_env_key_1[] = "VAR1";
|
||||
char test_env_key_2[] = "_VAR_2_";
|
||||
char test_env_key_3[] = "vAr#";
|
||||
char test_env_key_4[] = "FOO";
|
||||
char test_env_key_5[] = "ZOO";
|
||||
char test_env_key_6[] = "TR_MISSING_TEST_ENV_KEY";
|
||||
|
||||
char test_env_value_1[] = "value1 ";
|
||||
char test_env_value_2[] = " value2";
|
||||
char test_env_value_3[] = " \"value3'^! $PATH %PATH% ";
|
||||
char test_env_value_4[] = "bar";
|
||||
char test_env_value_5[] = "jar";
|
||||
|
||||
char* const args[] =
|
||||
{
|
||||
self_path,
|
||||
result_path,
|
||||
arg_dump_env,
|
||||
test_env_key_1,
|
||||
test_env_key_2,
|
||||
test_env_key_3,
|
||||
test_env_key_4,
|
||||
test_env_key_5,
|
||||
test_env_key_6,
|
||||
NULL
|
||||
};
|
||||
|
||||
char* const env[] =
|
||||
{
|
||||
tr_strdup_printf("%s=%s", test_env_key_1, test_env_value_1),
|
||||
tr_strdup_printf("%s=%s", test_env_key_2, test_env_value_2),
|
||||
tr_strdup_printf("%s=%s", test_env_key_3, test_env_value_3),
|
||||
tr_strdup_printf("%s=%s", test_env_key_5, test_env_value_5),
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Inherited */
|
||||
char foo_env_value[] = "FOO=bar";
|
||||
putenv(foo_env_value);
|
||||
|
||||
/* Overridden */
|
||||
char zoo_env_value[] = "ZOO=tar";
|
||||
putenv(zoo_env_value);
|
||||
|
||||
tr_error* error = NULL;
|
||||
bool const ret = tr_spawn_async(args, env, NULL, &error);
|
||||
check_bool(ret, ==, true);
|
||||
check_ptr(error, ==, NULL);
|
||||
|
||||
while (!tr_sys_path_exists(result_path, NULL))
|
||||
{
|
||||
tr_wait_msec(10);
|
||||
}
|
||||
|
||||
tr_sys_file_t fd = tr_sys_file_open(result_path, TR_SYS_FILE_READ, 0, NULL);
|
||||
check_int(fd, !=, TR_BAD_SYS_FILE);
|
||||
|
||||
char buffer[1024];
|
||||
|
||||
check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
check_str(buffer, ==, test_env_value_1);
|
||||
|
||||
check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
check_str(buffer, ==, test_env_value_2);
|
||||
|
||||
check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
check_str(buffer, ==, test_env_value_3);
|
||||
|
||||
check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
check_str(buffer, ==, test_env_value_4);
|
||||
|
||||
check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
check_str(buffer, ==, test_env_value_5);
|
||||
|
||||
check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
check_str(buffer, ==, "<null>");
|
||||
|
||||
check(!tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
|
||||
tr_sys_file_close(fd, NULL);
|
||||
|
||||
tr_free_ptrv((void* const*)env);
|
||||
tr_free(result_path);
|
||||
libtest_sandbox_destroy(test_dir);
|
||||
tr_free(test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_spawn_async_cwd_explicit(void)
|
||||
{
|
||||
char* const test_dir = libtest_sandbox_create();
|
||||
char* const result_path = tr_sys_path_native_separators(tr_buildPath(test_dir, "result.txt", NULL));
|
||||
|
||||
char* const args[] =
|
||||
{
|
||||
self_path,
|
||||
result_path,
|
||||
arg_dump_cwd,
|
||||
NULL
|
||||
};
|
||||
|
||||
tr_error* error = NULL;
|
||||
bool const ret = tr_spawn_async(args, NULL, test_dir, &error);
|
||||
check_bool(ret, ==, true);
|
||||
check_ptr(error, ==, NULL);
|
||||
|
||||
while (!tr_sys_path_exists(result_path, NULL))
|
||||
{
|
||||
tr_wait_msec(10);
|
||||
}
|
||||
|
||||
tr_sys_file_t fd = tr_sys_file_open(result_path, TR_SYS_FILE_READ, 0, NULL);
|
||||
check_int(fd, !=, TR_BAD_SYS_FILE);
|
||||
|
||||
char buffer[1024];
|
||||
|
||||
check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
check_str(tr_sys_path_native_separators(buffer), ==, tr_sys_path_native_separators(test_dir));
|
||||
|
||||
check(!tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
|
||||
tr_sys_file_close(fd, NULL);
|
||||
|
||||
tr_free(result_path);
|
||||
libtest_sandbox_destroy(test_dir);
|
||||
tr_free(test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_spawn_async_cwd_inherit(void)
|
||||
{
|
||||
char* const test_dir = libtest_sandbox_create();
|
||||
char* const result_path = tr_sys_path_native_separators(tr_buildPath(test_dir, "result.txt", NULL));
|
||||
|
||||
char* const expected_cwd = tr_sys_dir_get_current(NULL);
|
||||
|
||||
char* const args[] =
|
||||
{
|
||||
self_path,
|
||||
result_path,
|
||||
arg_dump_cwd,
|
||||
NULL
|
||||
};
|
||||
|
||||
tr_error* error = NULL;
|
||||
bool const ret = tr_spawn_async(args, NULL, NULL, &error);
|
||||
check_bool(ret, ==, true);
|
||||
check_ptr(error, ==, NULL);
|
||||
|
||||
while (!tr_sys_path_exists(result_path, NULL))
|
||||
{
|
||||
tr_wait_msec(10);
|
||||
}
|
||||
|
||||
tr_sys_file_t fd = tr_sys_file_open(result_path, TR_SYS_FILE_READ, 0, NULL);
|
||||
check_int(fd, !=, TR_BAD_SYS_FILE);
|
||||
|
||||
char buffer[1024];
|
||||
|
||||
check(tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
check_str(tr_sys_path_native_separators(buffer), ==, tr_sys_path_native_separators(expected_cwd));
|
||||
|
||||
check(!tr_sys_file_read_line(fd, buffer, sizeof(buffer), NULL));
|
||||
|
||||
tr_sys_file_close(fd, NULL);
|
||||
|
||||
tr_free(expected_cwd);
|
||||
tr_free(result_path);
|
||||
libtest_sandbox_destroy(test_dir);
|
||||
tr_free(test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_spawn_async_cwd_missing(void)
|
||||
{
|
||||
char* const test_dir = libtest_sandbox_create();
|
||||
char* const result_path = tr_sys_path_native_separators(tr_buildPath(test_dir, "result.txt", NULL));
|
||||
|
||||
char* const args[] =
|
||||
{
|
||||
self_path,
|
||||
result_path,
|
||||
arg_dump_cwd,
|
||||
NULL
|
||||
};
|
||||
|
||||
tr_error* error = NULL;
|
||||
bool const ret = tr_spawn_async(args, NULL, TR_IF_WIN32("C:\\", "/") "tr-missing-test-work-dir", &error);
|
||||
check_bool(ret, ==, false);
|
||||
check_ptr(error, !=, NULL);
|
||||
check_int(error->code, !=, 0);
|
||||
check_str(error->message, !=, NULL);
|
||||
|
||||
tr_error_clear(&error);
|
||||
|
||||
tr_free(result_path);
|
||||
libtest_sandbox_destroy(test_dir);
|
||||
tr_free(test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
self_path = tr_sys_path_resolve(argv[0], NULL);
|
||||
|
||||
if (argc >= 3)
|
||||
{
|
||||
char* const result_path = argv[1];
|
||||
char* const test_action = argv[2];
|
||||
|
||||
char* const tmp_result_path = tr_strdup_printf("%s.tmp", result_path);
|
||||
|
||||
tr_sys_file_t const fd = tr_sys_file_open(tmp_result_path, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE |
|
||||
TR_SYS_FILE_TRUNCATE, 0644, NULL);
|
||||
|
||||
if (fd == TR_BAD_SYS_FILE)
|
||||
{
|
||||
tr_free(tmp_result_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(test_action, arg_dump_args) == 0)
|
||||
{
|
||||
for (int i = 3; i < argc; ++i)
|
||||
{
|
||||
tr_sys_file_write_line(fd, argv[i], NULL);
|
||||
}
|
||||
}
|
||||
else if (strcmp(test_action, arg_dump_env) == 0)
|
||||
{
|
||||
for (int i = 3; i < argc; ++i)
|
||||
{
|
||||
char* const value = tr_env_get_string(argv[i], "<null>");
|
||||
tr_sys_file_write_line(fd, value, NULL);
|
||||
tr_free(value);
|
||||
}
|
||||
}
|
||||
else if (strcmp(test_action, arg_dump_cwd) == 0)
|
||||
{
|
||||
char* const value = tr_sys_dir_get_current(NULL);
|
||||
tr_sys_file_write_line(fd, value != NULL ? value : "<null>", NULL);
|
||||
tr_free(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_sys_file_close(fd, NULL);
|
||||
tr_sys_path_remove(tmp_result_path, NULL);
|
||||
|
||||
tr_free(tmp_result_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
tr_sys_file_close(fd, NULL);
|
||||
tr_sys_path_rename(tmp_result_path, result_path, NULL);
|
||||
|
||||
tr_free(tmp_result_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
testFunc const tests[] =
|
||||
{
|
||||
test_spawn_async_missing_exe,
|
||||
test_spawn_async_args,
|
||||
test_spawn_async_env,
|
||||
test_spawn_async_cwd_explicit,
|
||||
test_spawn_async_cwd_inherit,
|
||||
test_spawn_async_cwd_missing
|
||||
};
|
||||
|
||||
int ret = runTests(tests, NUM_TESTS(tests));
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
strcpy(self_path + strlen(self_path) - 4, ".cmd");
|
||||
|
||||
int ret2 = runTests(tests, NUM_TESTS(tests));
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
ret = ret2;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
tr_free(self_path);
|
||||
return ret;
|
||||
}
|
|
@ -8,4 +8,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
bool tr_spawn_async(char* const* cmd, char* const* env, char const* work_dir, struct tr_error** error);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1301,14 +1301,14 @@ tr_torrent_activity tr_torrentGetActivity(tr_torrent const* tor)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static time_t torrentGetIdleSecs(tr_torrent const* tor)
|
||||
static int torrentGetIdleSecs(tr_torrent const* tor)
|
||||
{
|
||||
int idle_secs;
|
||||
tr_torrent_activity const activity = tr_torrentGetActivity(tor);
|
||||
|
||||
if ((activity == TR_STATUS_DOWNLOAD || activity == TR_STATUS_SEED) && tor->startDate != 0)
|
||||
{
|
||||
idle_secs = difftime(tr_time(), MAX(tor->startDate, tor->activityDate));
|
||||
idle_secs = (int)difftime(tr_time(), MAX(tor->startDate, tor->activityDate));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -16,9 +16,12 @@
|
|||
#include "completion.h" /* tr_completion */
|
||||
#include "session.h" /* tr_sessionLock(), tr_sessionUnlock() */
|
||||
#include "tr-assert.h"
|
||||
#include "tr-macros.h"
|
||||
#include "utils.h" /* TR_GNUC_PRINTF */
|
||||
#include "ptrarray.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
struct tr_torrent_tiers;
|
||||
struct tr_magnet_info;
|
||||
|
||||
|
@ -466,3 +469,5 @@ static inline tr_direction tr_torrentGetQueueDirection(tr_torrent const* tor)
|
|||
{
|
||||
return tr_torrentIsSeed(tor) ? TR_UP : TR_DOWN;
|
||||
}
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
|
||||
static struct event* dht_timer = NULL;
|
||||
static unsigned char myid[20];
|
||||
static tr_session* session = NULL;
|
||||
static tr_session* session_ = NULL;
|
||||
|
||||
static void timer_callback(evutil_socket_t s, short type, void* ignore);
|
||||
|
||||
|
@ -145,7 +145,7 @@ static void bootstrap_from_name(char const* name, tr_port port, int af)
|
|||
|
||||
nap(15);
|
||||
|
||||
if (bootstrap_done(session, af))
|
||||
if (bootstrap_done(session_, af))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ static void dht_bootstrap(void* closure)
|
|||
int num = cl->len / 6;
|
||||
int num6 = cl->len6 / 18;
|
||||
|
||||
if (session != cl->session)
|
||||
if (session_ != cl->session)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -217,7 +217,7 @@ static void dht_bootstrap(void* closure)
|
|||
nap(15);
|
||||
}
|
||||
|
||||
if (bootstrap_done(session, 0))
|
||||
if (bootstrap_done(session_, 0))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -265,7 +265,7 @@ static void dht_bootstrap(void* closure)
|
|||
|
||||
*p = '\0';
|
||||
|
||||
bootstrap_from_name(buf, port, bootstrap_af(session));
|
||||
bootstrap_from_name(buf, port, bootstrap_af(session_));
|
||||
|
||||
if (bootstrap_done(cl->session, 0))
|
||||
{
|
||||
|
@ -299,7 +299,7 @@ static void dht_bootstrap(void* closure)
|
|||
tr_logAddNamedInfo("DHT", "Attempting bootstrap from dht.transmissionbt.com");
|
||||
}
|
||||
|
||||
bootstrap_from_name("dht.transmissionbt.com", 6881, bootstrap_af(session));
|
||||
bootstrap_from_name("dht.transmissionbt.com", 6881, bootstrap_af(session_));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,7 +330,7 @@ int tr_dhtInit(tr_session* ss)
|
|||
size_t len6;
|
||||
struct bootstrap_closure* cl;
|
||||
|
||||
if (session != NULL) /* already initialized */
|
||||
if (session_ != NULL) /* already initialized */
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
@ -397,17 +397,17 @@ int tr_dhtInit(tr_session* ss)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
session = ss;
|
||||
session_ = ss;
|
||||
|
||||
cl = tr_new(struct bootstrap_closure, 1);
|
||||
cl->session = session;
|
||||
cl->session = session_;
|
||||
cl->nodes = nodes;
|
||||
cl->nodes6 = nodes6;
|
||||
cl->len = len;
|
||||
cl->len6 = len6;
|
||||
tr_threadNew(dht_bootstrap, cl);
|
||||
|
||||
dht_timer = evtimer_new(session->event_base, timer_callback, session);
|
||||
dht_timer = evtimer_new(session_->event_base, timer_callback, session_);
|
||||
tr_timerAdd(dht_timer, 0, tr_rand_int_weak(1000000));
|
||||
|
||||
tr_logAddNamedDbg("DHT", "DHT initialized");
|
||||
|
@ -419,13 +419,13 @@ fail:
|
|||
tr_free(nodes);
|
||||
|
||||
tr_logAddNamedDbg("DHT", "DHT initialization failed (errno = %d)", errno);
|
||||
session = NULL;
|
||||
session_ = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void tr_dhtUninit(tr_session* ss)
|
||||
{
|
||||
if (session != ss)
|
||||
if (session_ != ss)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -492,12 +492,12 @@ void tr_dhtUninit(tr_session* ss)
|
|||
dht_uninit();
|
||||
tr_logAddNamedDbg("DHT", "Done uninitializing DHT");
|
||||
|
||||
session = NULL;
|
||||
session_ = NULL;
|
||||
}
|
||||
|
||||
bool tr_dhtEnabled(tr_session const* ss)
|
||||
{
|
||||
return ss != NULL && ss == session;
|
||||
return ss != NULL && ss == session_;
|
||||
}
|
||||
|
||||
struct getstatus_closure
|
||||
|
@ -644,8 +644,8 @@ static void callback(void* ignore UNUSED, int event, unsigned char const* info_h
|
|||
if (event == DHT_EVENT_VALUES || event == DHT_EVENT_VALUES6)
|
||||
{
|
||||
tr_torrent* tor;
|
||||
tr_sessionLock(session);
|
||||
tor = tr_torrentFindFromHash(session, info_hash);
|
||||
tr_sessionLock(session_);
|
||||
tor = tr_torrentFindFromHash(session_, info_hash);
|
||||
|
||||
if (tor != NULL && tr_torrentAllowsDHT(tor))
|
||||
{
|
||||
|
@ -670,11 +670,11 @@ static void callback(void* ignore UNUSED, int event, unsigned char const* info_h
|
|||
tr_logAddTorDbg(tor, "Learned %d %s peers from DHT", (int)n, event == DHT_EVENT_VALUES6 ? "IPv6" : "IPv4");
|
||||
}
|
||||
|
||||
tr_sessionUnlock(session);
|
||||
tr_sessionUnlock(session_);
|
||||
}
|
||||
else if (event == DHT_EVENT_SEARCH_DONE || event == DHT_EVENT_SEARCH_DONE6)
|
||||
{
|
||||
tr_torrent* tor = tr_torrentFindFromHash(session, info_hash);
|
||||
tr_torrent* tor = tr_torrentFindFromHash(session_, info_hash);
|
||||
|
||||
if (tor != NULL)
|
||||
{
|
||||
|
@ -714,7 +714,7 @@ static int tr_dhtAnnounce(tr_torrent* tor, int af, bool announce)
|
|||
|
||||
if (status >= TR_DHT_POOR)
|
||||
{
|
||||
rc = dht_search(tor->info.hash, announce ? tr_sessionGetPeerPort(session) : 0, af, callback, NULL);
|
||||
rc = dht_search(tor->info.hash, announce ? tr_sessionGetPeerPort(session_) : 0, af, callback, NULL);
|
||||
|
||||
if (rc >= 1)
|
||||
{
|
||||
|
@ -779,7 +779,7 @@ void tr_dhtCallback(unsigned char* buf, int buflen, struct sockaddr* from, sockl
|
|||
{
|
||||
TR_ASSERT(tr_isSession(sv));
|
||||
|
||||
if (sv != session)
|
||||
if (sv != session_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -808,7 +808,7 @@ void tr_dhtCallback(unsigned char* buf, int buflen, struct sockaddr* from, sockl
|
|||
|
||||
/* Being slightly late is fine,
|
||||
and has the added benefit of adding some jitter. */
|
||||
tr_timerAdd(dht_timer, tosleep, tr_rand_int_weak(1000000));
|
||||
tr_timerAdd(dht_timer, (int)tosleep, tr_rand_int_weak(1000000));
|
||||
}
|
||||
|
||||
static void timer_callback(evutil_socket_t s UNUSED, short type UNUSED, void* session)
|
||||
|
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "transmission.h"
|
||||
#include "tr-getopt.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
static struct tr_option const options[] =
|
||||
{
|
||||
{ 'p', "private", "Allow this torrent to only be used with the specified tracker(s)", "p", false, NULL },
|
||||
{ 'o', "outfile", "Save the generated .torrent to this filename", "o", true, "<file>" },
|
||||
{ 's', "piecesize", "Set how many KiB each piece should be, overriding the preferred default", "s", true, "<size in KiB>" },
|
||||
{ 'c', "comment", "Add a comment", "c", true, "<comment>" },
|
||||
{ 't', "tracker", "Add a tracker's announce URL", "t", true, "<url>" },
|
||||
{ 'q', "pooka", "Pooka", "pk", false, NULL },
|
||||
{ 'V', "version", "Show version number and exit", "V", false, NULL },
|
||||
{ 0, NULL, NULL, NULL, false, NULL }
|
||||
};
|
||||
|
||||
static int run_test(int argc, char const** argv, int expected_n, int* expected_c, char const** expected_optarg)
|
||||
{
|
||||
int c;
|
||||
int n;
|
||||
char const* optarg;
|
||||
|
||||
n = 0;
|
||||
tr_optind = 1;
|
||||
|
||||
while ((c = tr_getopt("summary", argc, argv, options, &optarg)) != TR_OPT_DONE)
|
||||
{
|
||||
check_int(n, <, expected_n);
|
||||
check_int(c, ==, expected_c[n]);
|
||||
check_str(optarg, ==, expected_optarg[n]);
|
||||
++n;
|
||||
}
|
||||
|
||||
check_int(n, ==, expected_n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static int test_no_options(void)
|
||||
{
|
||||
int argc = 1;
|
||||
char const* argv[] = { "/some/path/tr-getopt-test" };
|
||||
int expected_n = 0;
|
||||
int expected_c[] = { 0 };
|
||||
char const* expected_optarg[] = { NULL };
|
||||
return run_test(argc, argv, expected_n, expected_c, expected_optarg);
|
||||
}
|
||||
|
||||
static int test_short_noarg(void)
|
||||
{
|
||||
int argc = 2;
|
||||
char const* argv[] = { "/some/path/tr-getopt-test", "-p" };
|
||||
int expected_n = 1;
|
||||
int expected_c[] = { 'p' };
|
||||
char const* expected_optarg[] = { NULL };
|
||||
return run_test(argc, argv, expected_n, expected_c, expected_optarg);
|
||||
}
|
||||
|
||||
static int test_long_noarg(void)
|
||||
{
|
||||
int argc = 2;
|
||||
char const* argv[] = { "/some/path/tr-getopt-test", "--private" };
|
||||
int expected_n = 1;
|
||||
int expected_c[] = { 'p' };
|
||||
char const* expected_optarg[] = { NULL };
|
||||
return run_test(argc, argv, expected_n, expected_c, expected_optarg);
|
||||
}
|
||||
|
||||
static int test_short_with_arg(void)
|
||||
{
|
||||
int argc = 3;
|
||||
char const* argv[] = { "/some/path/tr-getopt-test", "-o", "/tmp/outfile" };
|
||||
int expected_n = 1;
|
||||
int expected_c[] = { 'o' };
|
||||
char const* expected_optarg[] = { "/tmp/outfile" };
|
||||
return run_test(argc, argv, expected_n, expected_c, expected_optarg);
|
||||
}
|
||||
|
||||
static int test_long_with_arg(void)
|
||||
{
|
||||
int argc = 3;
|
||||
char const* argv[] = { "/some/path/tr-getopt-test", "--outfile", "/tmp/outfile" };
|
||||
int expected_n = 1;
|
||||
int expected_c[] = { 'o' };
|
||||
char const* expected_optarg[] = { "/tmp/outfile" };
|
||||
return run_test(argc, argv, expected_n, expected_c, expected_optarg);
|
||||
}
|
||||
|
||||
static int test_short_with_arg_after_eq(void)
|
||||
{
|
||||
int argc = 2;
|
||||
char const* argv[] = { "/some/path/tr-getopt-test", "-o=/tmp/outfile" };
|
||||
int expected_n = 1;
|
||||
int expected_c[] = { 'o' };
|
||||
char const* expected_optarg[] = { "/tmp/outfile" };
|
||||
return run_test(argc, argv, expected_n, expected_c, expected_optarg);
|
||||
}
|
||||
|
||||
static int test_long_with_arg_after_eq(void)
|
||||
{
|
||||
int argc = 2;
|
||||
char const* argv[] = { "/some/path/tr-getopt-test", "--outfile=/tmp/outfile" };
|
||||
int expected_n = 1;
|
||||
int expected_c[] = { 'o' };
|
||||
char const* expected_optarg[] = { "/tmp/outfile" };
|
||||
return run_test(argc, argv, expected_n, expected_c, expected_optarg);
|
||||
}
|
||||
|
||||
static int test_unknown_option(void)
|
||||
{
|
||||
int argc = 2;
|
||||
char const* argv[] = { "/some/path/tr-getopt-test", "-z" };
|
||||
int expected_n = 1;
|
||||
int expected_c[] = { TR_OPT_UNK };
|
||||
char const* expected_optarg[] = { "-z" };
|
||||
return run_test(argc, argv, expected_n, expected_c, expected_optarg);
|
||||
}
|
||||
|
||||
static int test_missing_arg(void)
|
||||
{
|
||||
int argc = 2;
|
||||
char const* argv[] = { "/some/path/tr-getopt-test", "-o" };
|
||||
int expected_n = 1;
|
||||
int expected_c[] = { TR_OPT_ERR };
|
||||
char const* expected_optarg[] = { NULL };
|
||||
return run_test(argc, argv, expected_n, expected_c, expected_optarg);
|
||||
}
|
||||
|
||||
static int test_lots_of_options(void)
|
||||
{
|
||||
int argc = 6;
|
||||
char const* argv[] = { "/some/path/tr-getopt-test", "--piecesize=4", "-c", "hello world", "-p", "--tracker=foo" };
|
||||
int expected_n = 4;
|
||||
int expected_c[] = { 's', 'c', 'p', 't' };
|
||||
char const* expected_optarg[] = { "4", "hello world", NULL, "foo" };
|
||||
return run_test(argc, argv, expected_n, expected_c, expected_optarg);
|
||||
}
|
||||
|
||||
static int test_match_longer_key(void)
|
||||
{
|
||||
/* confirm that this resolves to 'q' and not 'p' */
|
||||
int argc = 2;
|
||||
char const* argv[] = { "/some/path/tr-getopt-test", "-pk" };
|
||||
int expected_n = 1;
|
||||
int expected_c[] = { 'q' };
|
||||
char const* expected_optarg[] = { NULL };
|
||||
return run_test(argc, argv, expected_n, expected_c, expected_optarg);
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testFunc const tests[] =
|
||||
{
|
||||
test_no_options,
|
||||
test_short_noarg,
|
||||
test_long_noarg,
|
||||
test_short_with_arg,
|
||||
test_long_with_arg,
|
||||
test_short_with_arg_after_eq,
|
||||
test_long_with_arg_after_eq,
|
||||
test_unknown_option,
|
||||
test_missing_arg,
|
||||
test_match_longer_key,
|
||||
test_lots_of_options
|
||||
};
|
||||
|
||||
return runTests(tests, NUM_TESTS(tests));
|
||||
}
|
|
@ -8,10 +8,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* @addtogroup utils Utilities
|
||||
|
@ -52,8 +51,6 @@ int tr_getopt(char const* summary, int argc, char const* const* argv, tr_option
|
|||
/** @brief prints the `Usage' help section to stdout */
|
||||
void tr_getopt_usage(char const* appName, char const* description, tr_option const* opts);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -173,3 +173,12 @@
|
|||
#define TR_INET6_ADDRSTRLEN 46
|
||||
|
||||
#define TR_BAD_SIZE ((size_t)-1)
|
||||
|
||||
/* Guard C code in headers, while including them from C++ */
|
||||
#ifdef __cplusplus
|
||||
#define TR_BEGIN_DECLS extern "C" {
|
||||
#define TR_END_DECLS }
|
||||
#else
|
||||
#define TR_BEGIN_DECLS
|
||||
#define TR_END_DECLS
|
||||
#endif
|
||||
|
|
|
@ -32,6 +32,7 @@ THE SOFTWARE.
|
|||
|
||||
#include <event2/event.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <libutp/utp.h>
|
||||
|
||||
#include "transmission.h"
|
||||
|
|
|
@ -23,6 +23,7 @@ THE SOFTWARE.
|
|||
|
||||
#include <event2/event.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <libutp/utp.h>
|
||||
|
||||
#include "transmission.h"
|
||||
|
@ -36,12 +37,12 @@ THE SOFTWARE.
|
|||
#include "tr-utp.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifndef WITH_UTP
|
||||
|
||||
#define MY_NAME "UTP"
|
||||
|
||||
#define dbgmsg(...) tr_logAddDeepNamed(MY_NAME, __VA_ARGS__)
|
||||
|
||||
#ifndef WITH_UTP
|
||||
|
||||
void UTP_Close(struct UTPSocket* socket)
|
||||
{
|
||||
tr_logAddNamedError(MY_NAME, "UTP_Close(%p) was called.", socket);
|
||||
|
|
|
@ -14,11 +14,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/***
|
||||
****
|
||||
**** Basic Types
|
||||
|
@ -32,6 +27,8 @@ extern "C"
|
|||
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
typedef uint32_t tr_file_index_t;
|
||||
typedef uint32_t tr_piece_index_t;
|
||||
/* assuming a 16 KiB block, a 32-bit block index gives us a maximum torrent size of 63 TiB.
|
||||
|
@ -721,6 +718,7 @@ void tr_sessionSetTorrentDoneScript(tr_session*, char const* scriptFilename);
|
|||
|
||||
typedef enum
|
||||
{
|
||||
TR_LOG_SILENT = 0,
|
||||
TR_LOG_ERROR = 1,
|
||||
TR_LOG_INFO = 2,
|
||||
TR_LOG_DEBUG = 3,
|
||||
|
@ -1888,6 +1886,4 @@ static inline bool tr_isDirection(tr_direction d)
|
|||
return d == TR_UP || d == TR_DOWN;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -12,8 +12,9 @@
|
|||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
/**
|
||||
**/
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
void tr_eventInit(tr_session*);
|
||||
|
||||
|
@ -22,3 +23,5 @@ void tr_eventClose(tr_session*);
|
|||
bool tr_amInEventThread(tr_session const*);
|
||||
|
||||
void tr_runInEventThread(tr_session*, void (* func)(void*), void* user_data);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -1,588 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <limits.h> /* INT_MAX */
|
||||
#include <math.h> /* sqrt() */
|
||||
#include <string.h> /* strlen() */
|
||||
#include <stdlib.h> /* setenv(), unsetenv() */
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define setenv(key, value, unused) SetEnvironmentVariableA(key, value)
|
||||
#define unsetenv(key) SetEnvironmentVariableA(key, NULL)
|
||||
#endif
|
||||
|
||||
#include "transmission.h"
|
||||
#include "ConvertUTF.h" /* tr_utf8_validate*/
|
||||
#include "platform.h"
|
||||
#include "crypto-utils.h" /* tr_rand_int_weak */
|
||||
#include "utils.h"
|
||||
#include "web.h"
|
||||
|
||||
#define SPEED_TEST 0
|
||||
|
||||
#if SPEED_TEST
|
||||
#define VERBOSE
|
||||
#endif
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
static int test_strip_positional_args(void)
|
||||
{
|
||||
char const* in;
|
||||
char const* out;
|
||||
char const* expected;
|
||||
|
||||
in = "Hello %1$s foo %2$.*f";
|
||||
expected = "Hello %s foo %.*f";
|
||||
out = tr_strip_positional_args(in);
|
||||
check_str(out, ==, expected);
|
||||
|
||||
in = "Hello %1$'d foo %2$'f";
|
||||
expected = "Hello %d foo %f";
|
||||
out = tr_strip_positional_args(in);
|
||||
check_str(out, ==, expected);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_strstrip(void)
|
||||
{
|
||||
char* in;
|
||||
char* out;
|
||||
|
||||
/* strstrip */
|
||||
in = tr_strdup(" test ");
|
||||
out = tr_strstrip(in);
|
||||
check_ptr(in, ==, out);
|
||||
check_str(out, ==, "test");
|
||||
tr_free(in);
|
||||
|
||||
/* strstrip */
|
||||
in = tr_strdup(" test test ");
|
||||
out = tr_strstrip(in);
|
||||
check_ptr(in, ==, out);
|
||||
check_str(out, ==, "test test");
|
||||
tr_free(in);
|
||||
|
||||
/* strstrip */
|
||||
in = tr_strdup("test");
|
||||
out = tr_strstrip(in);
|
||||
check_ptr(in, ==, out);
|
||||
check_str(out, ==, "test");
|
||||
tr_free(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_strjoin(void)
|
||||
{
|
||||
char* out;
|
||||
|
||||
char const* in1[] = { "one", "two" };
|
||||
out = tr_strjoin(in1, 2, ", ");
|
||||
check_str(out, ==, "one, two");
|
||||
tr_free(out);
|
||||
|
||||
char const* in2[] = { "hello" };
|
||||
out = tr_strjoin(in2, 1, "###");
|
||||
check_str(out, ==, "hello");
|
||||
tr_free(out);
|
||||
|
||||
char const* in3[] = { "a", "b", "ccc", "d", "eeeee" };
|
||||
out = tr_strjoin(in3, 5, " ");
|
||||
check_str(out, ==, "a b ccc d eeeee");
|
||||
tr_free(out);
|
||||
|
||||
char const* in4[] = { "7", "ate", "9" };
|
||||
out = tr_strjoin(in4, 3, "");
|
||||
check_str(out, ==, "7ate9");
|
||||
tr_free(out);
|
||||
|
||||
char const** in5;
|
||||
out = tr_strjoin(in5, 0, "a");
|
||||
check_str(out, ==, "");
|
||||
tr_free(out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_buildpath(void)
|
||||
{
|
||||
char* out;
|
||||
|
||||
out = tr_buildPath("foo", "bar", NULL);
|
||||
check_str(out, ==, "foo" TR_PATH_DELIMITER_STR "bar");
|
||||
tr_free(out);
|
||||
|
||||
out = tr_buildPath("", "foo", "bar", NULL);
|
||||
check_str(out, ==, TR_PATH_DELIMITER_STR "foo" TR_PATH_DELIMITER_STR "bar");
|
||||
tr_free(out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_utf8(void)
|
||||
{
|
||||
char const* in;
|
||||
char* out;
|
||||
|
||||
in = "hello world";
|
||||
out = tr_utf8clean(in, TR_BAD_SIZE);
|
||||
check_str(out, ==, in);
|
||||
tr_free(out);
|
||||
|
||||
in = "hello world";
|
||||
out = tr_utf8clean(in, 5);
|
||||
check_str(out, ==, "hello");
|
||||
tr_free(out);
|
||||
|
||||
/* this version is not utf-8 (but cp866) */
|
||||
in = "\x92\xE0\xE3\xA4\xAD\xAE \xA1\xEB\xE2\xEC \x81\xAE\xA3\xAE\xAC";
|
||||
out = tr_utf8clean(in, 17);
|
||||
check_ptr(out, !=, NULL);
|
||||
check(strlen(out) == 17 || strlen(out) == 33);
|
||||
check(tr_utf8_validate(out, TR_BAD_SIZE, NULL));
|
||||
tr_free(out);
|
||||
|
||||
/* same string, but utf-8 clean */
|
||||
in = "Трудно быть Богом";
|
||||
out = tr_utf8clean(in, TR_BAD_SIZE);
|
||||
check_ptr(out, !=, NULL);
|
||||
check(tr_utf8_validate(out, TR_BAD_SIZE, NULL));
|
||||
check_str(out, ==, in);
|
||||
tr_free(out);
|
||||
|
||||
in = "\xF4\x00\x81\x82";
|
||||
out = tr_utf8clean(in, 4);
|
||||
check_ptr(out, !=, NULL);
|
||||
check(strlen(out) == 1 || strlen(out) == 2);
|
||||
check(tr_utf8_validate(out, TR_BAD_SIZE, NULL));
|
||||
tr_free(out);
|
||||
|
||||
in = "\xF4\x33\x81\x82";
|
||||
out = tr_utf8clean(in, 4);
|
||||
check_ptr(out, !=, NULL);
|
||||
check(strlen(out) == 4 || strlen(out) == 7);
|
||||
check(tr_utf8_validate(out, TR_BAD_SIZE, NULL));
|
||||
tr_free(out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_numbers(void)
|
||||
{
|
||||
int count;
|
||||
int* numbers;
|
||||
|
||||
numbers = tr_parseNumberRange("1-10,13,16-19", TR_BAD_SIZE, &count);
|
||||
check_int(count, ==, 15);
|
||||
check_int(numbers[0], ==, 1);
|
||||
check_int(numbers[5], ==, 6);
|
||||
check_int(numbers[9], ==, 10);
|
||||
check_int(numbers[10], ==, 13);
|
||||
check_int(numbers[11], ==, 16);
|
||||
check_int(numbers[14], ==, 19);
|
||||
tr_free(numbers);
|
||||
|
||||
numbers = tr_parseNumberRange("1-5,3-7,2-6", TR_BAD_SIZE, &count);
|
||||
check_int(count, ==, 7);
|
||||
check_ptr(numbers, !=, NULL);
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
check_int(numbers[i], ==, i + 1);
|
||||
}
|
||||
|
||||
tr_free(numbers);
|
||||
|
||||
numbers = tr_parseNumberRange("1-Hello", TR_BAD_SIZE, &count);
|
||||
check_int(count, ==, 0);
|
||||
check_ptr(numbers, ==, NULL);
|
||||
|
||||
numbers = tr_parseNumberRange("1-", TR_BAD_SIZE, &count);
|
||||
check_int(count, ==, 0);
|
||||
check_ptr(numbers, ==, NULL);
|
||||
|
||||
numbers = tr_parseNumberRange("Hello", TR_BAD_SIZE, &count);
|
||||
check_int(count, ==, 0);
|
||||
check_ptr(numbers, ==, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int compareInts(void const* va, void const* vb)
|
||||
{
|
||||
int const a = *(int const*)va;
|
||||
int const b = *(int const*)vb;
|
||||
return a - b;
|
||||
}
|
||||
|
||||
static int test_lowerbound(void)
|
||||
{
|
||||
int const A[] = { 1, 2, 3, 3, 3, 5, 8 };
|
||||
int const expected_pos[] = { 0, 1, 2, 5, 5, 6, 6, 6, 7, 7 };
|
||||
bool const expected_exact[] = { true, true, true, false, true, false, false, true, false, false };
|
||||
int const N = TR_N_ELEMENTS(A);
|
||||
|
||||
for (int i = 1; i <= 10; i++)
|
||||
{
|
||||
bool exact;
|
||||
int const pos = tr_lowerBound(&i, A, N, sizeof(int), compareInts, &exact);
|
||||
|
||||
#if 0
|
||||
|
||||
fprintf(stderr, "searching for %d. ", i);
|
||||
fprintf(stderr, "result: index = %d, ", pos);
|
||||
|
||||
if (pos != N)
|
||||
{
|
||||
fprintf(stderr, "A[%d] == %d\n", pos, A[pos]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "which is off the end.\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
check_int(pos, ==, expected_pos[i - 1]);
|
||||
check_int(exact, ==, expected_exact[i - 1]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_quickFindFirst_Iteration(size_t const k, size_t const n, int* buf, int range)
|
||||
{
|
||||
int highest_low;
|
||||
int lowest_high;
|
||||
|
||||
/* populate buf with random ints */
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
buf[i] = tr_rand_int_weak(range);
|
||||
}
|
||||
|
||||
/* find the best k */
|
||||
tr_quickfindFirstK(buf, n, sizeof(int), compareInts, k);
|
||||
|
||||
/* confirm that the smallest K ints are in the first slots K slots in buf */
|
||||
|
||||
highest_low = INT_MIN;
|
||||
|
||||
for (size_t i = 0; i < k; ++i)
|
||||
{
|
||||
if (highest_low < buf[i])
|
||||
{
|
||||
highest_low = buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
lowest_high = INT_MAX;
|
||||
|
||||
for (size_t i = k; i < n; ++i)
|
||||
{
|
||||
if (lowest_high > buf[i])
|
||||
{
|
||||
lowest_high = buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
check_int(highest_low, <=, lowest_high);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_quickfindFirst(void)
|
||||
{
|
||||
size_t const k = 10;
|
||||
size_t const n = 100;
|
||||
size_t const n_trials = 1000;
|
||||
int* buf = tr_new(int, n);
|
||||
|
||||
for (size_t i = 0; i < n_trials; ++i)
|
||||
{
|
||||
check_int(test_quickFindFirst_Iteration(k, n, buf, 100), ==, 0);
|
||||
}
|
||||
|
||||
tr_free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_memmem(void)
|
||||
{
|
||||
char const haystack[12] = "abcabcabcabc";
|
||||
char const needle[3] = "cab";
|
||||
|
||||
check_ptr(tr_memmem(haystack, sizeof(haystack), haystack, sizeof(haystack)), ==, haystack);
|
||||
check_ptr(tr_memmem(haystack, sizeof(haystack), needle, sizeof(needle)), ==, haystack + 2);
|
||||
check_ptr(tr_memmem(needle, sizeof(needle), haystack, sizeof(haystack)), ==, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_hex(void)
|
||||
{
|
||||
char hex1[41];
|
||||
char hex2[41];
|
||||
uint8_t binary[20];
|
||||
|
||||
memcpy(hex1, "fb5ef5507427b17e04b69cef31fa3379b456735a", 41);
|
||||
tr_hex_to_binary(hex1, binary, 20);
|
||||
tr_binary_to_hex(binary, hex2, 20);
|
||||
check_str(hex1, ==, hex2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_array(void)
|
||||
{
|
||||
size_t array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
size_t n = TR_N_ELEMENTS(array);
|
||||
|
||||
tr_removeElementFromArray(array, 5U, sizeof(size_t), n);
|
||||
--n;
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
check_int(array[i], ==, i < 5 ? i : i + 1);
|
||||
}
|
||||
|
||||
tr_removeElementFromArray(array, 0U, sizeof(size_t), n);
|
||||
--n;
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
check_int(array[i], ==, i < 4 ? i + 1 : i + 2);
|
||||
}
|
||||
|
||||
tr_removeElementFromArray(array, n - 1, sizeof(size_t), n);
|
||||
--n;
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
check_int(array[i], ==, i < 4 ? i + 1 : i + 2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_url(void)
|
||||
{
|
||||
int port;
|
||||
char* scheme;
|
||||
char* host;
|
||||
char* path;
|
||||
char* str;
|
||||
char const* url;
|
||||
|
||||
url = "http://1";
|
||||
check(tr_urlParse(url, TR_BAD_SIZE, &scheme, &host, &port, &path));
|
||||
check_str(scheme, ==, "http");
|
||||
check_str(host, ==, "1");
|
||||
check_str(path, ==, "/");
|
||||
check_int(port, ==, 80);
|
||||
tr_free(scheme);
|
||||
tr_free(path);
|
||||
tr_free(host);
|
||||
|
||||
url = "http://www.some-tracker.org/some/path";
|
||||
check(tr_urlParse(url, TR_BAD_SIZE, &scheme, &host, &port, &path));
|
||||
check_str(scheme, ==, "http");
|
||||
check_str(host, ==, "www.some-tracker.org");
|
||||
check_str(path, ==, "/some/path");
|
||||
check_int(port, ==, 80);
|
||||
tr_free(scheme);
|
||||
tr_free(path);
|
||||
tr_free(host);
|
||||
|
||||
url = "http://www.some-tracker.org:80/some/path";
|
||||
check(tr_urlParse(url, TR_BAD_SIZE, &scheme, &host, &port, &path));
|
||||
check_str(scheme, ==, "http");
|
||||
check_str(host, ==, "www.some-tracker.org");
|
||||
check_str(path, ==, "/some/path");
|
||||
check_int(port, ==, 80);
|
||||
tr_free(scheme);
|
||||
tr_free(path);
|
||||
tr_free(host);
|
||||
|
||||
url = "http%3A%2F%2Fwww.example.com%2F~user%2F%3Ftest%3D1%26test1%3D2";
|
||||
str = tr_http_unescape(url, strlen(url));
|
||||
check_str(str, ==, "http://www.example.com/~user/?test=1&test1=2");
|
||||
tr_free(str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_truncd(void)
|
||||
{
|
||||
char buf[32];
|
||||
double const nan = sqrt(-1);
|
||||
|
||||
tr_snprintf(buf, sizeof(buf), "%.2f%%", 99.999);
|
||||
check_str(buf, ==, "100.00%");
|
||||
|
||||
tr_snprintf(buf, sizeof(buf), "%.2f%%", tr_truncd(99.999, 2));
|
||||
check_str(buf, ==, "99.99%");
|
||||
|
||||
tr_snprintf(buf, sizeof(buf), "%.4f", tr_truncd(403650.656250, 4));
|
||||
check_str(buf, ==, "403650.6562");
|
||||
|
||||
tr_snprintf(buf, sizeof(buf), "%.2f", tr_truncd(2.15, 2));
|
||||
check_str(buf, ==, "2.15");
|
||||
|
||||
tr_snprintf(buf, sizeof(buf), "%.2f", tr_truncd(2.05, 2));
|
||||
check_str(buf, ==, "2.05");
|
||||
|
||||
tr_snprintf(buf, sizeof(buf), "%.2f", tr_truncd(3.3333, 2));
|
||||
check_str(buf, ==, "3.33");
|
||||
|
||||
tr_snprintf(buf, sizeof(buf), "%.0f", tr_truncd(3.3333, 0));
|
||||
check_str(buf, ==, "3");
|
||||
|
||||
tr_snprintf(buf, sizeof(buf), "%.0f", tr_truncd(3.9999, 0));
|
||||
check_str(buf, ==, "3");
|
||||
|
||||
#if !(defined(_MSC_VER) || (defined(__MINGW32__) && defined(__MSVCRT__)))
|
||||
/* FIXME: MSCVRT behaves differently in case of nan */
|
||||
tr_snprintf(buf, sizeof(buf), "%.2f", tr_truncd(nan, 2));
|
||||
check(strstr(buf, "nan") != NULL || strstr(buf, "NaN") != NULL);
|
||||
#else
|
||||
(void)nan;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char* test_strdup_printf_valist(char const* fmt, ...) TR_GNUC_PRINTF(1, 2);
|
||||
|
||||
static char* test_strdup_printf_valist(char const* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char* ret;
|
||||
|
||||
va_start(args, fmt);
|
||||
ret = tr_strdup_vprintf(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_strdup_printf(void)
|
||||
{
|
||||
char* s;
|
||||
char* s2;
|
||||
char* s3;
|
||||
|
||||
s = tr_strdup_printf("%s", "test");
|
||||
check_str(s, ==, "test");
|
||||
tr_free(s);
|
||||
|
||||
s = tr_strdup_printf("%d %s %c %u", -1, "0", '1', 2);
|
||||
check_str(s, ==, "-1 0 1 2");
|
||||
tr_free(s);
|
||||
|
||||
s3 = tr_malloc0(4098);
|
||||
memset(s3, '-', 4097);
|
||||
s3[2047] = 't';
|
||||
s3[2048] = 'e';
|
||||
s3[2049] = 's';
|
||||
s3[2050] = 't';
|
||||
|
||||
s2 = tr_malloc0(4096);
|
||||
memset(s2, '-', 4095);
|
||||
s2[2047] = '%';
|
||||
s2[2048] = 's';
|
||||
|
||||
s = tr_strdup_printf(s2, "test");
|
||||
check_str(s, ==, s3);
|
||||
tr_free(s);
|
||||
|
||||
tr_free(s2);
|
||||
|
||||
s = tr_strdup_printf("%s", s3);
|
||||
check_str(s, ==, s3);
|
||||
tr_free(s);
|
||||
|
||||
tr_free(s3);
|
||||
|
||||
s = test_strdup_printf_valist("\n-%s-%s-%s-\n", "\r", "\t", "\b");
|
||||
check_str(s, ==, "\n-\r-\t-\b-\n");
|
||||
tr_free(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_env(void)
|
||||
{
|
||||
char const* test_key = "TR_TEST_ENV";
|
||||
int x;
|
||||
char* s;
|
||||
|
||||
unsetenv(test_key);
|
||||
|
||||
check(!tr_env_key_exists(test_key));
|
||||
x = tr_env_get_int(test_key, 123);
|
||||
check_int(x, ==, 123);
|
||||
s = tr_env_get_string(test_key, NULL);
|
||||
check_str(s, ==, NULL);
|
||||
s = tr_env_get_string(test_key, "a");
|
||||
check_str(s, ==, "a");
|
||||
tr_free(s);
|
||||
|
||||
setenv(test_key, "", 1);
|
||||
|
||||
check(tr_env_key_exists(test_key));
|
||||
x = tr_env_get_int(test_key, 456);
|
||||
check_int(x, ==, 456);
|
||||
s = tr_env_get_string(test_key, NULL);
|
||||
check_str(s, ==, "");
|
||||
tr_free(s);
|
||||
s = tr_env_get_string(test_key, "b");
|
||||
check_str(s, ==, "");
|
||||
tr_free(s);
|
||||
|
||||
setenv(test_key, "135", 1);
|
||||
|
||||
check(tr_env_key_exists(test_key));
|
||||
x = tr_env_get_int(test_key, 789);
|
||||
check_int(x, ==, 135);
|
||||
s = tr_env_get_string(test_key, NULL);
|
||||
check_str(s, ==, "135");
|
||||
tr_free(s);
|
||||
s = tr_env_get_string(test_key, "c");
|
||||
check_str(s, ==, "135");
|
||||
tr_free(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testFunc const tests[] =
|
||||
{
|
||||
test_array,
|
||||
test_buildpath,
|
||||
test_hex,
|
||||
test_lowerbound,
|
||||
test_quickfindFirst,
|
||||
test_memmem,
|
||||
test_numbers,
|
||||
test_strip_positional_args,
|
||||
test_strdup_printf,
|
||||
test_strstrip,
|
||||
test_strjoin,
|
||||
test_truncd,
|
||||
test_url,
|
||||
test_utf8,
|
||||
test_env
|
||||
};
|
||||
|
||||
return runTests(tests, NUM_TESTS(tests));
|
||||
}
|
|
@ -702,7 +702,7 @@ void tr_wait_msec(long int msec)
|
|||
****
|
||||
***/
|
||||
|
||||
int tr_snprintf(char* buf, size_t buflen, char const* fmt, ...)
|
||||
int tr_snprintf(void* buf, size_t buflen, char const* fmt, ...)
|
||||
{
|
||||
int len;
|
||||
va_list args;
|
||||
|
@ -718,7 +718,7 @@ int tr_snprintf(char* buf, size_t buflen, char const* fmt, ...)
|
|||
* will be copied. Always NUL terminates (unless siz == 0).
|
||||
* Returns strlen (src); if retval >= siz, truncation occurred.
|
||||
*/
|
||||
size_t tr_strlcpy(char* dst, void const* src, size_t siz)
|
||||
size_t tr_strlcpy(void* dst, void const* src, size_t siz)
|
||||
{
|
||||
TR_ASSERT(dst != NULL);
|
||||
TR_ASSERT(src != NULL);
|
||||
|
@ -787,35 +787,39 @@ double tr_getRatio(uint64_t numerator, uint64_t denominator)
|
|||
return ratio;
|
||||
}
|
||||
|
||||
void tr_binary_to_hex(void const* input, char* output, size_t byte_length)
|
||||
void tr_binary_to_hex(void const* vinput, void* voutput, size_t byte_length)
|
||||
{
|
||||
static char const hex[] = "0123456789abcdef";
|
||||
uint8_t const* input_octets = input;
|
||||
|
||||
uint8_t const* input = vinput;
|
||||
char* output = voutput;
|
||||
|
||||
/* go from back to front to allow for in-place conversion */
|
||||
input_octets += byte_length;
|
||||
input += byte_length;
|
||||
output += byte_length * 2;
|
||||
|
||||
*output = '\0';
|
||||
|
||||
while (byte_length-- > 0)
|
||||
{
|
||||
unsigned int const val = *(--input_octets);
|
||||
unsigned int const val = *(--input);
|
||||
*(--output) = hex[val & 0xf];
|
||||
*(--output) = hex[val >> 4];
|
||||
}
|
||||
}
|
||||
|
||||
void tr_hex_to_binary(char const* input, void* output, size_t byte_length)
|
||||
void tr_hex_to_binary(void const* vinput, void* voutput, size_t byte_length)
|
||||
{
|
||||
static char const hex[] = "0123456789abcdef";
|
||||
uint8_t* output_octets = output;
|
||||
|
||||
uint8_t const* input = (uint8_t const*)vinput;
|
||||
uint8_t* output = voutput;
|
||||
|
||||
for (size_t i = 0; i < byte_length; ++i)
|
||||
{
|
||||
int const hi = strchr(hex, tolower(*input++)) - hex;
|
||||
int const lo = strchr(hex, tolower(*input++)) - hex;
|
||||
*output_octets++ = (uint8_t)((hi << 4) | lo);
|
||||
*output++ = (uint8_t)((hi << 4) | lo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1020,7 +1024,7 @@ bool tr_urlParse(char const* url, size_t url_len, char** setme_scheme, char** se
|
|||
****
|
||||
***/
|
||||
|
||||
void tr_removeElementFromArray(void* array, unsigned int index_to_remove, size_t sizeof_element, size_t nmemb)
|
||||
void tr_removeElementFromArray(void* array, size_t index_to_remove, size_t sizeof_element, size_t nmemb)
|
||||
{
|
||||
char* a = array;
|
||||
|
||||
|
|
|
@ -13,10 +13,9 @@
|
|||
#include <stddef.h> /* size_t */
|
||||
#include <time.h> /* time_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
/***
|
||||
****
|
||||
|
@ -229,10 +228,10 @@ char* tr_strdup_printf(char const* fmt, ...) TR_GNUC_MALLOC TR_GNUC_PRINTF(1, 2)
|
|||
char* tr_strdup_vprintf(char const* fmt, va_list args) TR_GNUC_MALLOC TR_GNUC_PRINTF(1, 0);
|
||||
|
||||
/** @brief Portability wrapper for strlcpy() that uses the system implementation if available */
|
||||
size_t tr_strlcpy(char* dst, void const* src, size_t siz);
|
||||
size_t tr_strlcpy(void* dst, void const* src, size_t siz);
|
||||
|
||||
/** @brief Portability wrapper for snprintf() that uses the system implementation if available */
|
||||
int tr_snprintf(char* buf, size_t buflen, char const* fmt, ...) TR_GNUC_PRINTF(3, 4) TR_GNUC_NONNULL(1, 3);
|
||||
int tr_snprintf(void* buf, size_t buflen, char const* fmt, ...) TR_GNUC_PRINTF(3, 4) TR_GNUC_NONNULL(1, 3);
|
||||
|
||||
/** @brief Convenience wrapper around strerorr() guaranteed to not return NULL
|
||||
@param errnum the error number to describe */
|
||||
|
@ -263,8 +262,8 @@ char* tr_strjoin(char const* const* arr, size_t len, char const* delim);
|
|||
|
||||
int compareInt(void const* va, void const* vb);
|
||||
|
||||
void tr_binary_to_hex(void const* input, char* output, size_t byte_length) TR_GNUC_NONNULL(1, 2);
|
||||
void tr_hex_to_binary(char const* input, void* output, size_t byte_length) TR_GNUC_NONNULL(1, 2);
|
||||
void tr_binary_to_hex(void const* input, void* output, size_t byte_length) TR_GNUC_NONNULL(1, 2);
|
||||
void tr_hex_to_binary(void const* input, void* output, size_t byte_length) TR_GNUC_NONNULL(1, 2);
|
||||
|
||||
/** @brief convenience function to determine if an address is an IP address (IPv4 or IPv6) */
|
||||
bool tr_addressIsIP(char const* address);
|
||||
|
@ -334,7 +333,7 @@ int tr_gettimeofday(struct timeval* tv);
|
|||
bool tr_moveFile(char const* oldpath, char const* newpath, struct tr_error** error) TR_GNUC_NONNULL(1, 2);
|
||||
|
||||
/** @brief convenience function to remove an item from an array */
|
||||
void tr_removeElementFromArray(void* array, unsigned int index_to_remove, size_t sizeof_element, size_t nmemb);
|
||||
void tr_removeElementFromArray(void* array, size_t index_to_remove, size_t sizeof_element, size_t nmemb);
|
||||
|
||||
/***
|
||||
****
|
||||
|
@ -389,6 +388,7 @@ extern unsigned int tr_size_K;
|
|||
/* format a speed from KBps into a user-readable string. */
|
||||
char* tr_formatter_speed_KBps(char* buf, double KBps, size_t buflen);
|
||||
|
||||
// FIXME(ckerr): bytes should be a size_t
|
||||
/* format a memory size from bytes into a user-readable string. */
|
||||
char* tr_formatter_mem_B(char* buf, int64_t bytes, size_t buflen);
|
||||
|
||||
|
@ -398,6 +398,7 @@ static inline char* tr_formatter_mem_MB(char* buf, double MBps, size_t buflen)
|
|||
return tr_formatter_mem_B(buf, (int64_t)(MBps * tr_mem_K * tr_mem_K), buflen);
|
||||
}
|
||||
|
||||
// FIXME(ckerr): bytes should be a size_t
|
||||
/* format a file size from bytes into a user-readable string. */
|
||||
char* tr_formatter_size_B(char* buf, int64_t bytes, size_t buflen);
|
||||
|
||||
|
@ -422,12 +423,6 @@ char* tr_env_get_string(char const* key, char const* default_value);
|
|||
|
||||
void tr_net_init(void);
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#include "ConvertUTF.h"
|
||||
|
||||
#define __LIBTRANSMISSION_VARIANT_MODULE__
|
||||
#define LIBTRANSMISSION_VARIANT_MODULE
|
||||
|
||||
#include "transmission.h"
|
||||
#include "ptrarray.h"
|
||||
|
@ -39,8 +39,10 @@
|
|||
* but to handle it as a signed 64bit integer is mandatory to handle
|
||||
* "large files" aka .torrent for more that 4Gbyte
|
||||
*/
|
||||
int tr_bencParseInt(uint8_t const* buf, uint8_t const* bufend, uint8_t const** setme_end, int64_t* setme_val)
|
||||
int tr_bencParseInt(void const* vbuf, void const* vbufend, uint8_t const** setme_end, int64_t* setme_val)
|
||||
{
|
||||
uint8_t const* const buf = (uint8_t const*)vbuf;
|
||||
uint8_t const* const bufend = (uint8_t const*)vbufend;
|
||||
char* endptr;
|
||||
void const* begin;
|
||||
void const* end;
|
||||
|
@ -88,9 +90,12 @@ int tr_bencParseInt(uint8_t const* buf, uint8_t const* bufend, uint8_t const** s
|
|||
* Note that there is no constant beginning delimiter, and no ending delimiter.
|
||||
* Example: 4:spam represents the string "spam"
|
||||
*/
|
||||
int tr_bencParseStr(uint8_t const* buf, uint8_t const* bufend, uint8_t const** setme_end, uint8_t const** setme_str,
|
||||
int tr_bencParseStr(void const* vbuf, void const* vbufend, uint8_t const** setme_end, uint8_t const** setme_str,
|
||||
size_t* setme_strlen)
|
||||
{
|
||||
uint8_t const* const buf = (uint8_t const*)vbuf;
|
||||
uint8_t const* const bufend = (uint8_t const*)vbufend;
|
||||
|
||||
void const* end;
|
||||
size_t len;
|
||||
char* ulend;
|
||||
|
|
|
@ -8,10 +8,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef __LIBTRANSMISSION_VARIANT_MODULE__
|
||||
#ifndef LIBTRANSMISSION_VARIANT_MODULE
|
||||
#error only libtransmission/variant-*.c should #include this header.
|
||||
#endif
|
||||
|
||||
#include "tr-macros.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
typedef void (* VariantWalkFunc)(tr_variant const* val, void* user_data);
|
||||
|
||||
struct VariantWalkFuncs
|
||||
|
@ -37,10 +41,12 @@ void tr_variantInit(tr_variant* v, char type);
|
|||
int tr_jsonParse(char const* source, void const* vbuf, size_t len, tr_variant* setme_benc, char const** setme_end);
|
||||
|
||||
/** @brief Private function that's exposed here only for unit tests */
|
||||
int tr_bencParseInt(uint8_t const* buf, uint8_t const* bufend, uint8_t const** setme_end, int64_t* setme_val);
|
||||
int tr_bencParseInt(void const* buf, void const* bufend, uint8_t const** setme_end, int64_t* setme_val);
|
||||
|
||||
/** @brief Private function that's exposed here only for unit tests */
|
||||
int tr_bencParseStr(uint8_t const* buf, uint8_t const* bufend, uint8_t const** setme_end, uint8_t const** setme_str,
|
||||
int tr_bencParseStr(void const* buf, void const* bufend, uint8_t const** setme_end, uint8_t const** setme_str,
|
||||
size_t* setme_strlen);
|
||||
|
||||
int tr_variantParseBenc(void const* buf, void const* end, tr_variant* top, char const** setme_end);
|
||||
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -15,14 +15,11 @@
|
|||
#include <event2/buffer.h> /* evbuffer_add() */
|
||||
#include <event2/util.h> /* evutil_strtoll() */
|
||||
|
||||
#define JSONSL_STATE_USER_FIELDS /* no fields */
|
||||
#include "jsonsl.h"
|
||||
#include "jsonsl.c"
|
||||
|
||||
#define __LIBTRANSMISSION_VARIANT_MODULE__
|
||||
#define LIBTRANSMISSION_VARIANT_MODULE
|
||||
|
||||
#include "transmission.h"
|
||||
#include "ConvertUTF.h"
|
||||
#include "jsonsl.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
#include "ptrarray.h"
|
||||
|
@ -246,8 +243,7 @@ static char* extract_escaped_string(char const* in, size_t in_len, size_t* len,
|
|||
|
||||
if (ConvertUTF32toUTF8(&str32_walk, str32_end, &str8_walk, str8_end, 0) == 0)
|
||||
{
|
||||
size_t const len = str8_walk - str8_buf;
|
||||
evbuffer_add(buf, str8_buf, len);
|
||||
evbuffer_add(buf, str8_buf, str8_walk - str8_buf);
|
||||
unescaped = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,598 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2013-2014 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ctype.h> /* isspace() */
|
||||
#include <errno.h> /* EILSEQ */
|
||||
#include <string.h> /* strlen(), strncmp() */
|
||||
|
||||
#include <event2/buffer.h>
|
||||
|
||||
#define __LIBTRANSMISSION_VARIANT_MODULE__
|
||||
|
||||
#include "transmission.h"
|
||||
#include "utils.h" /* tr_free */
|
||||
#include "variant.h"
|
||||
#include "variant-common.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#define STACK_SMASH_DEPTH (1 * 1000 * 1000)
|
||||
#else
|
||||
#define STACK_SMASH_DEPTH (100 * 1000)
|
||||
#endif
|
||||
|
||||
static int testInt(void)
|
||||
{
|
||||
uint8_t buf[128];
|
||||
int64_t val;
|
||||
int err;
|
||||
uint8_t const* end;
|
||||
|
||||
/* good int string */
|
||||
tr_snprintf((char*)buf, sizeof(buf), "i64e");
|
||||
err = tr_bencParseInt(buf, buf + 4, &end, &val);
|
||||
check_int(err, ==, 0);
|
||||
check_int(val, ==, 64);
|
||||
check_ptr(end, ==, buf + 4);
|
||||
|
||||
/* missing 'e' */
|
||||
end = NULL;
|
||||
val = 888;
|
||||
err = tr_bencParseInt(buf, buf + 3, &end, &val);
|
||||
check_int(err, ==, EILSEQ);
|
||||
check_int(val, ==, 888);
|
||||
check_ptr(end, ==, NULL);
|
||||
|
||||
/* empty buffer */
|
||||
err = tr_bencParseInt(buf, buf + 0, &end, &val);
|
||||
check_int(err, ==, EILSEQ);
|
||||
check_int(val, ==, 888);
|
||||
check_ptr(end, ==, NULL);
|
||||
|
||||
/* bad number */
|
||||
tr_snprintf((char*)buf, sizeof(buf), "i6z4e");
|
||||
err = tr_bencParseInt(buf, buf + 5, &end, &val);
|
||||
check_int(err, ==, EILSEQ);
|
||||
check_int(val, ==, 888);
|
||||
check_ptr(end, ==, NULL);
|
||||
|
||||
/* negative number */
|
||||
tr_snprintf((char*)buf, sizeof(buf), "i-3e");
|
||||
err = tr_bencParseInt(buf, buf + 4, &end, &val);
|
||||
check_int(err, ==, 0);
|
||||
check_int(val, ==, -3);
|
||||
check_ptr(end, ==, buf + 4);
|
||||
|
||||
/* zero */
|
||||
tr_snprintf((char*)buf, sizeof(buf), "i0e");
|
||||
err = tr_bencParseInt(buf, buf + 4, &end, &val);
|
||||
check_int(err, ==, 0);
|
||||
check_int(val, ==, 0);
|
||||
check_ptr(end, ==, buf + 3);
|
||||
|
||||
/* no leading zeroes allowed */
|
||||
val = 0;
|
||||
end = NULL;
|
||||
tr_snprintf((char*)buf, sizeof(buf), "i04e");
|
||||
err = tr_bencParseInt(buf, buf + 4, &end, &val);
|
||||
check_int(err, ==, EILSEQ);
|
||||
check_int(val, ==, 0);
|
||||
check_ptr(end, ==, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int testStr(void)
|
||||
{
|
||||
uint8_t buf[128];
|
||||
int err;
|
||||
int n;
|
||||
uint8_t const* end;
|
||||
uint8_t const* str;
|
||||
size_t len;
|
||||
|
||||
/* string len is designed to overflow */
|
||||
n = tr_snprintf((char*)buf, sizeof(buf), "%zu:boat", (size_t)(SIZE_MAX - 2));
|
||||
err = tr_bencParseStr(buf, buf + n, &end, &str, &len);
|
||||
check_int(err, ==, EILSEQ);
|
||||
check_uint(len, ==, 0);
|
||||
check_ptr(str, ==, NULL);
|
||||
check_ptr(end, ==, NULL);
|
||||
|
||||
/* good string */
|
||||
n = tr_snprintf((char*)buf, sizeof(buf), "4:boat");
|
||||
err = tr_bencParseStr(buf, buf + n, &end, &str, &len);
|
||||
check_int(err, ==, 0);
|
||||
check_uint(len, ==, 4);
|
||||
check_mem(str, ==, "boat", len);
|
||||
check_ptr(end, ==, buf + 6);
|
||||
str = NULL;
|
||||
end = NULL;
|
||||
len = 0;
|
||||
|
||||
/* string goes past end of buffer */
|
||||
err = tr_bencParseStr(buf, buf + (n - 1), &end, &str, &len);
|
||||
check_int(err, ==, EILSEQ);
|
||||
check_uint(len, ==, 0);
|
||||
check_ptr(str, ==, NULL);
|
||||
check_ptr(end, ==, NULL);
|
||||
|
||||
/* empty string */
|
||||
n = tr_snprintf((char*)buf, sizeof(buf), "0:");
|
||||
err = tr_bencParseStr(buf, buf + n, &end, &str, &len);
|
||||
check_int(err, ==, 0);
|
||||
check_uint(len, ==, 0);
|
||||
check_uint(*str, ==, '\0');
|
||||
check_ptr(end, ==, buf + 2);
|
||||
str = NULL;
|
||||
end = NULL;
|
||||
len = 0;
|
||||
|
||||
/* short string */
|
||||
n = tr_snprintf((char*)buf, sizeof(buf), "3:boat");
|
||||
err = tr_bencParseStr(buf, buf + n, &end, &str, &len);
|
||||
check_int(err, ==, 0);
|
||||
check_uint(len, ==, 3);
|
||||
check_mem(str, ==, "boa", len);
|
||||
check_ptr(end, ==, buf + 5);
|
||||
str = NULL;
|
||||
end = NULL;
|
||||
len = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int testString(char const* str, bool isGood)
|
||||
{
|
||||
tr_variant val;
|
||||
char const* end = NULL;
|
||||
char* saved;
|
||||
size_t const len = strlen(str);
|
||||
size_t savedLen;
|
||||
int err;
|
||||
|
||||
err = tr_variantFromBencFull(&val, str, len, NULL, &end);
|
||||
|
||||
if (!isGood)
|
||||
{
|
||||
check_int(err, !=, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
check_int(err, ==, 0);
|
||||
#if 0
|
||||
fprintf(stderr, "in: [%s]\n", str);
|
||||
fprintf(stderr, "out:\n%s", tr_variantToStr(&val, TR_VARIANT_FMT_JSON, NULL));
|
||||
#endif
|
||||
check_ptr(end, ==, str + len);
|
||||
saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &savedLen);
|
||||
check_str(saved, ==, str);
|
||||
check_uint(savedLen, ==, len);
|
||||
tr_free(saved);
|
||||
tr_variantFree(&val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int testParse(void)
|
||||
{
|
||||
tr_variant val;
|
||||
tr_variant* child;
|
||||
tr_variant* child2;
|
||||
char buf[512];
|
||||
char const* end;
|
||||
int err;
|
||||
size_t len;
|
||||
int64_t i;
|
||||
char* saved;
|
||||
|
||||
tr_snprintf((char*)buf, sizeof(buf), "i64e");
|
||||
err = tr_variantFromBencFull(&val, buf, sizeof(buf), NULL, &end);
|
||||
check_int(err, ==, 0);
|
||||
check(tr_variantGetInt(&val, &i));
|
||||
check_int(i, ==, 64);
|
||||
check_ptr(end, ==, buf + 4);
|
||||
tr_variantFree(&val);
|
||||
|
||||
tr_snprintf((char*)buf, sizeof(buf), "li64ei32ei16ee");
|
||||
err = tr_variantFromBencFull(&val, buf, sizeof(buf), NULL, &end);
|
||||
check_int(err, ==, 0);
|
||||
check_ptr(end, ==, buf + strlen((char*)buf));
|
||||
check_uint(val.val.l.count, ==, 3);
|
||||
check(tr_variantGetInt(&val.val.l.vals[0], &i));
|
||||
check_int(i, ==, 64);
|
||||
check(tr_variantGetInt(&val.val.l.vals[1], &i));
|
||||
check_int(i, ==, 32);
|
||||
check(tr_variantGetInt(&val.val.l.vals[2], &i));
|
||||
check_int(i, ==, 16);
|
||||
saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len);
|
||||
check_str(saved, ==, buf);
|
||||
tr_free(saved);
|
||||
tr_variantFree(&val);
|
||||
|
||||
end = NULL;
|
||||
tr_snprintf((char*)buf, sizeof(buf), "lllee");
|
||||
err = tr_variantFromBencFull(&val, buf, sizeof(buf), NULL, &end);
|
||||
check_int(err, !=, 0);
|
||||
check_ptr(end, ==, NULL);
|
||||
|
||||
end = NULL;
|
||||
tr_snprintf((char*)buf, sizeof(buf), "le");
|
||||
err = tr_variantFromBencFull(&val, buf, sizeof(buf), NULL, &end);
|
||||
check_int(err, ==, 0);
|
||||
check_ptr(end, ==, buf + 2);
|
||||
saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len);
|
||||
check_str(saved, ==, "le");
|
||||
tr_free(saved);
|
||||
tr_variantFree(&val);
|
||||
|
||||
if ((err = testString("llleee", true)) != 0)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = testString("d3:cow3:moo4:spam4:eggse", true)) != 0)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = testString("d4:spaml1:a1:bee", true)) != 0)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = testString("d5:greenli1ei2ei3ee4:spamd1:ai123e3:keyi214eee", true)) != 0)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = testString("d9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee", true)) != 0)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = testString("d8:completei1e8:intervali1800e12:min intervali1800e5:peers0:e", true)) != 0)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = testString("d1:ai0e1:be", false)) != 0) /* odd number of children */
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = testString("", false)) != 0)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if ((err = testString(" ", false)) != 0)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
/* nested containers
|
||||
* parse an unsorted dict
|
||||
* save as a sorted dict */
|
||||
end = NULL;
|
||||
tr_snprintf((char*)buf, sizeof(buf), "lld1:bi32e1:ai64eeee");
|
||||
err = tr_variantFromBencFull(&val, buf, sizeof(buf), NULL, &end);
|
||||
check_int(err, ==, 0);
|
||||
check_ptr(end, ==, buf + strlen((char const*)buf));
|
||||
check_ptr((child = tr_variantListChild(&val, 0)), !=, NULL);
|
||||
check_ptr((child2 = tr_variantListChild(child, 0)), !=, NULL);
|
||||
saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len);
|
||||
check_str(saved, ==, "lld1:ai64e1:bi32eeee");
|
||||
tr_free(saved);
|
||||
tr_variantFree(&val);
|
||||
|
||||
/* too many endings */
|
||||
end = NULL;
|
||||
tr_snprintf((char*)buf, sizeof(buf), "leee");
|
||||
err = tr_variantFromBencFull(&val, buf, sizeof(buf), NULL, &end);
|
||||
check_int(err, ==, 0);
|
||||
check_ptr(end, ==, buf + 2);
|
||||
saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len);
|
||||
check_str(saved, ==, "le");
|
||||
tr_free(saved);
|
||||
tr_variantFree(&val);
|
||||
|
||||
/* no ending */
|
||||
end = NULL;
|
||||
tr_snprintf((char*)buf, sizeof(buf), "l1:a1:b1:c");
|
||||
err = tr_variantFromBencFull(&val, buf, strlen(buf), NULL, &end);
|
||||
check_int(err, !=, 0);
|
||||
|
||||
/* incomplete string */
|
||||
end = NULL;
|
||||
tr_snprintf((char*)buf, sizeof(buf), "1:");
|
||||
err = tr_variantFromBencFull(&val, buf, strlen(buf), NULL, &end);
|
||||
check_int(err, !=, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stripWhitespace(char* in)
|
||||
{
|
||||
char* out = in;
|
||||
|
||||
for (; !tr_str_is_empty(in); ++in)
|
||||
{
|
||||
if (!isspace(*in))
|
||||
{
|
||||
*out++ = *in;
|
||||
}
|
||||
}
|
||||
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
static int testJSONSnippet(char const* benc_str, char const* expected)
|
||||
{
|
||||
tr_variant top;
|
||||
char* serialized;
|
||||
struct evbuffer* buf;
|
||||
|
||||
tr_variantFromBenc(&top, benc_str, strlen(benc_str));
|
||||
buf = tr_variantToBuf(&top, TR_VARIANT_FMT_JSON);
|
||||
serialized = (char*)evbuffer_pullup(buf, -1);
|
||||
stripWhitespace(serialized);
|
||||
#if 0
|
||||
fprintf(stderr, "benc: %s\n", benc_str);
|
||||
fprintf(stderr, "json: %s\n", serialized);
|
||||
fprintf(stderr, "want: %s\n", expected);
|
||||
#endif
|
||||
check_str(serialized, ==, expected);
|
||||
tr_variantFree(&top);
|
||||
evbuffer_free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int testJSON(void)
|
||||
{
|
||||
int val;
|
||||
char const* benc_str;
|
||||
char const* expected;
|
||||
|
||||
benc_str = "i6e";
|
||||
expected = "6";
|
||||
|
||||
if ((val = testJSONSnippet(benc_str, expected)) != 0)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
benc_str = "d5:helloi1e5:worldi2ee";
|
||||
expected = "{\"hello\":1,\"world\":2}";
|
||||
|
||||
if ((val = testJSONSnippet(benc_str, expected)) != 0)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
benc_str = "d5:helloi1e5:worldi2e3:fooli1ei2ei3eee";
|
||||
expected = "{\"foo\":[1,2,3],\"hello\":1,\"world\":2}";
|
||||
|
||||
if ((val = testJSONSnippet(benc_str, expected)) != 0)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
benc_str = "d5:helloi1e5:worldi2e3:fooli1ei2ei3ed1:ai0eeee";
|
||||
expected = "{\"foo\":[1,2,3,{\"a\":0}],\"hello\":1,\"world\":2}";
|
||||
|
||||
if ((val = testJSONSnippet(benc_str, expected)) != 0)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
benc_str = "d4:argsd6:statusle7:status2lee6:result7:successe";
|
||||
expected = "{\"args\":{\"status\":[],\"status2\":[]},\"result\":\"success\"}";
|
||||
|
||||
if ((val = testJSONSnippet(benc_str, expected)) != 0)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int testMerge(void)
|
||||
{
|
||||
size_t len;
|
||||
tr_variant dest;
|
||||
tr_variant src;
|
||||
int64_t i;
|
||||
char const* s;
|
||||
tr_quark const i1 = tr_quark_new("i1", 2);
|
||||
tr_quark const i2 = tr_quark_new("i2", 2);
|
||||
tr_quark const i3 = tr_quark_new("i3", 2);
|
||||
tr_quark const i4 = tr_quark_new("i4", 2);
|
||||
tr_quark const s5 = tr_quark_new("s5", 2);
|
||||
tr_quark const s6 = tr_quark_new("s6", 2);
|
||||
tr_quark const s7 = tr_quark_new("s7", 2);
|
||||
tr_quark const s8 = tr_quark_new("s8", 2);
|
||||
|
||||
/* initial dictionary (default values) */
|
||||
tr_variantInitDict(&dest, 10);
|
||||
tr_variantDictAddInt(&dest, i1, 1);
|
||||
tr_variantDictAddInt(&dest, i2, 2);
|
||||
tr_variantDictAddInt(&dest, i4, -35); /* remains untouched */
|
||||
tr_variantDictAddStr(&dest, s5, "abc");
|
||||
tr_variantDictAddStr(&dest, s6, "def");
|
||||
tr_variantDictAddStr(&dest, s7, "127.0.0.1"); /* remains untouched */
|
||||
|
||||
/* new dictionary, will overwrite items in dest */
|
||||
tr_variantInitDict(&src, 10);
|
||||
tr_variantDictAddInt(&src, i1, 1); /* same value */
|
||||
tr_variantDictAddInt(&src, i2, 4); /* new value */
|
||||
tr_variantDictAddInt(&src, i3, 3); /* new key:value */
|
||||
tr_variantDictAddStr(&src, s5, "abc"); /* same value */
|
||||
tr_variantDictAddStr(&src, s6, "xyz"); /* new value */
|
||||
tr_variantDictAddStr(&src, s8, "ghi"); /* new key:value */
|
||||
|
||||
tr_variantMergeDicts(&dest, /*const*/ &src);
|
||||
|
||||
check(tr_variantDictFindInt(&dest, i1, &i));
|
||||
check_int(i, ==, 1);
|
||||
check(tr_variantDictFindInt(&dest, i2, &i));
|
||||
check_int(i, ==, 4);
|
||||
check(tr_variantDictFindInt(&dest, i3, &i));
|
||||
check_int(i, ==, 3);
|
||||
check(tr_variantDictFindInt(&dest, i4, &i));
|
||||
check_int(i, ==, -35);
|
||||
check(tr_variantDictFindStr(&dest, s5, &s, &len));
|
||||
check_uint(len, ==, 3);
|
||||
check_str(s, ==, "abc");
|
||||
check(tr_variantDictFindStr(&dest, s6, &s, &len));
|
||||
check_uint(len, ==, 3);
|
||||
check_str(s, ==, "xyz");
|
||||
check(tr_variantDictFindStr(&dest, s7, &s, &len));
|
||||
check_uint(len, ==, 9);
|
||||
check_str(s, ==, "127.0.0.1");
|
||||
check(tr_variantDictFindStr(&dest, s8, &s, &len));
|
||||
check_uint(len, ==, 3);
|
||||
check_str(s, ==, "ghi");
|
||||
|
||||
tr_variantFree(&dest);
|
||||
tr_variantFree(&src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int testStackSmash(void)
|
||||
{
|
||||
size_t len;
|
||||
int err;
|
||||
char* in;
|
||||
char const* end;
|
||||
tr_variant val;
|
||||
char* saved;
|
||||
int const depth = STACK_SMASH_DEPTH;
|
||||
|
||||
in = tr_new(char, depth * 2 + 1);
|
||||
|
||||
for (int i = 0; i < depth; ++i)
|
||||
{
|
||||
in[i] = 'l';
|
||||
in[depth + i] = 'e';
|
||||
}
|
||||
|
||||
in[depth * 2] = '\0';
|
||||
err = tr_variantFromBencFull(&val, in, depth * 2, NULL, &end);
|
||||
check_int(err, ==, 0);
|
||||
check_ptr(end, ==, in + depth * 2);
|
||||
saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len);
|
||||
check_str(saved, ==, in);
|
||||
tr_free(in);
|
||||
tr_free(saved);
|
||||
tr_variantFree(&val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int testBool(void)
|
||||
{
|
||||
tr_variant top;
|
||||
int64_t intVal;
|
||||
bool boolVal;
|
||||
tr_quark const key1 = tr_quark_new("key1", 4);
|
||||
tr_quark const key2 = tr_quark_new("key2", 4);
|
||||
tr_quark const key3 = tr_quark_new("key3", 4);
|
||||
tr_quark const key4 = tr_quark_new("key4", 4);
|
||||
|
||||
tr_variantInitDict(&top, 0);
|
||||
|
||||
tr_variantDictAddBool(&top, key1, false);
|
||||
tr_variantDictAddBool(&top, key2, 0);
|
||||
tr_variantDictAddInt(&top, key3, true);
|
||||
tr_variantDictAddInt(&top, key4, 1);
|
||||
check(tr_variantDictFindBool(&top, key1, &boolVal));
|
||||
check(!boolVal);
|
||||
check(tr_variantDictFindBool(&top, key2, &boolVal));
|
||||
check(!boolVal);
|
||||
check(tr_variantDictFindBool(&top, key3, &boolVal));
|
||||
check(boolVal);
|
||||
check(tr_variantDictFindBool(&top, key4, &boolVal));
|
||||
check(boolVal);
|
||||
check(tr_variantDictFindInt(&top, key1, &intVal));
|
||||
check_int(intVal, ==, 0);
|
||||
check(tr_variantDictFindInt(&top, key2, &intVal));
|
||||
check_int(intVal, ==, 0);
|
||||
check(tr_variantDictFindInt(&top, key3, &intVal));
|
||||
check_int(intVal, !=, 0);
|
||||
check(tr_variantDictFindInt(&top, key4, &intVal));
|
||||
check_int(intVal, !=, 0);
|
||||
|
||||
tr_variantFree(&top);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int testParse2(void)
|
||||
{
|
||||
tr_variant top;
|
||||
tr_variant top2;
|
||||
int64_t intVal;
|
||||
char const* strVal;
|
||||
double realVal;
|
||||
bool boolVal;
|
||||
size_t len;
|
||||
char* benc;
|
||||
char const* end;
|
||||
size_t strLen;
|
||||
tr_quark const key_bool = tr_quark_new("this-is-a-bool", TR_BAD_SIZE);
|
||||
tr_quark const key_real = tr_quark_new("this-is-a-real", TR_BAD_SIZE);
|
||||
tr_quark const key_int = tr_quark_new("this-is-an-int", TR_BAD_SIZE);
|
||||
tr_quark const key_str = tr_quark_new("this-is-a-string", TR_BAD_SIZE);
|
||||
|
||||
tr_variantInitDict(&top, 0);
|
||||
tr_variantDictAddBool(&top, key_bool, true);
|
||||
tr_variantDictAddInt(&top, key_int, 1234);
|
||||
tr_variantDictAddReal(&top, key_real, 0.5);
|
||||
tr_variantDictAddStr(&top, key_str, "this-is-a-string");
|
||||
|
||||
benc = tr_variantToStr(&top, TR_VARIANT_FMT_BENC, &len);
|
||||
check_str(benc, ==, "d14:this-is-a-booli1e14:this-is-a-real8:0.50000016:this-is-a-string16:this-is-a-string14:this-is-an-"
|
||||
"inti1234ee");
|
||||
check_int(tr_variantFromBencFull(&top2, benc, len, NULL, &end), ==, 0);
|
||||
check_ptr(end, ==, benc + len);
|
||||
check(tr_variantIsDict(&top2));
|
||||
check(tr_variantDictFindInt(&top, key_int, &intVal));
|
||||
check_int(intVal, ==, 1234);
|
||||
check(tr_variantDictFindBool(&top, key_bool, &boolVal));
|
||||
check(boolVal);
|
||||
check(tr_variantDictFindStr(&top, key_str, &strVal, &strLen));
|
||||
check_uint(strLen, ==, 16);
|
||||
check_str(strVal, ==, "this-is-a-string");
|
||||
check(tr_variantDictFindReal(&top, key_real, &realVal));
|
||||
check_int((int)(realVal * 100), ==, 50);
|
||||
|
||||
tr_variantFree(&top2);
|
||||
tr_free(benc);
|
||||
tr_variantFree(&top);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
static testFunc const tests[] =
|
||||
{
|
||||
testInt,
|
||||
testStr,
|
||||
testParse,
|
||||
testJSON,
|
||||
testMerge,
|
||||
testBool,
|
||||
testParse2,
|
||||
testStackSmash
|
||||
};
|
||||
|
||||
return runTests(tests, NUM_TESTS(tests));
|
||||
}
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
#include <event2/buffer.h>
|
||||
|
||||
#define __LIBTRANSMISSION_VARIANT_MODULE__
|
||||
#define LIBTRANSMISSION_VARIANT_MODULE
|
||||
|
||||
#include "transmission.h"
|
||||
#include "ConvertUTF.h"
|
||||
|
@ -821,13 +821,13 @@ static void nodeDestruct(struct SaveNode* node)
|
|||
* easier to read, but was vulnerable to a smash-stacking
|
||||
* attack via maliciously-crafted data. (#667)
|
||||
*/
|
||||
void tr_variantWalk(tr_variant const* v, struct VariantWalkFuncs const* walkFuncs, void* user_data, bool sort_dicts)
|
||||
void tr_variantWalk(tr_variant const* v_in, struct VariantWalkFuncs const* walkFuncs, void* user_data, bool sort_dicts)
|
||||
{
|
||||
int stackSize = 0;
|
||||
int stackAlloc = 64;
|
||||
struct SaveNode* stack = tr_new(struct SaveNode, stackAlloc);
|
||||
|
||||
nodeConstruct(&stack[stackSize++], v, sort_dicts);
|
||||
nodeConstruct(&stack[stackSize++], v_in, sort_dicts);
|
||||
|
||||
while (stackSize > 0)
|
||||
{
|
||||
|
|
|
@ -8,14 +8,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include <inttypes.h> /* int64_t */
|
||||
|
||||
#include <inttypes.h> /* for int64_t */
|
||||
#include "tr-macros.h"
|
||||
#include "quark.h"
|
||||
|
||||
TR_BEGIN_DECLS
|
||||
|
||||
struct evbuffer;
|
||||
|
||||
struct tr_error;
|
||||
|
@ -280,6 +279,4 @@ void tr_variantMergeDicts(tr_variant* dict_target, tr_variant const* dict_source
|
|||
|
||||
/* @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
TR_END_DECLS
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifndef __LIBTRANSMISSION_WATCHDIR_MODULE__
|
||||
#ifndef LIBTRANSMISSION_WATCHDIR_MODULE
|
||||
#error only the libtransmission watchdir module should #include this header.
|
||||
#endif
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#include <event2/event.h>
|
||||
|
||||
#define __LIBTRANSMISSION_WATCHDIR_MODULE__
|
||||
#define LIBTRANSMISSION_WATCHDIR_MODULE
|
||||
|
||||
#include "transmission.h"
|
||||
#include "log.h"
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <event2/bufferevent.h>
|
||||
#include <event2/event.h>
|
||||
|
||||
#define __LIBTRANSMISSION_WATCHDIR_MODULE__
|
||||
#define LIBTRANSMISSION_WATCHDIR_MODULE
|
||||
|
||||
#include "transmission.h"
|
||||
#include "log.h"
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#include <event2/event.h>
|
||||
|
||||
#define __LIBTRANSMISSION_WATCHDIR_MODULE__
|
||||
#define LIBTRANSMISSION_WATCHDIR_MODULE
|
||||
|
||||
#include "transmission.h"
|
||||
#include "log.h"
|
||||
|
|
|
@ -1,400 +0,0 @@
|
|||
/*
|
||||
* This file Copyright (C) 2015-2016 Mnemosyne LLC
|
||||
*
|
||||
* It may be used under the GNU GPL versions 2 or 3
|
||||
* or any future license endorsed by Mnemosyne LLC.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <event2/event.h>
|
||||
|
||||
#include "transmission.h"
|
||||
#include "file.h"
|
||||
#include "net.h"
|
||||
#include "utils.h"
|
||||
#include "watchdir.h"
|
||||
|
||||
#include "libtransmission-test.h"
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
typedef struct callback_data
|
||||
{
|
||||
tr_watchdir_t dir;
|
||||
char* name;
|
||||
tr_watchdir_status result;
|
||||
}
|
||||
callback_data;
|
||||
|
||||
#define CB_DATA_STATIC_INIT { NULL, NULL, 0 }
|
||||
|
||||
static struct event_base* ev_base = NULL;
|
||||
|
||||
extern struct timeval tr_watchdir_generic_interval;
|
||||
extern unsigned int tr_watchdir_retry_limit;
|
||||
extern struct timeval tr_watchdir_retry_start_interval;
|
||||
extern struct timeval tr_watchdir_retry_max_interval;
|
||||
|
||||
static struct timeval const FIFTY_MSEC = { .tv_sec = 0, .tv_usec = 50000 };
|
||||
static struct timeval const ONE_HUNDRED_MSEC = { .tv_sec = 0, .tv_usec = 100000 };
|
||||
static struct timeval const TWO_HUNDRED_MSEC = { .tv_sec = 0, .tv_usec = 200000 };
|
||||
|
||||
static void process_events(void)
|
||||
{
|
||||
event_base_loopexit(ev_base, &TWO_HUNDRED_MSEC);
|
||||
event_base_dispatch(ev_base);
|
||||
}
|
||||
|
||||
static tr_watchdir_status callback(tr_watchdir_t dir, char const* name, void* context)
|
||||
{
|
||||
callback_data* const data = context;
|
||||
|
||||
if (data->result != TR_WATCHDIR_RETRY)
|
||||
{
|
||||
data->dir = dir;
|
||||
|
||||
if (data->name != NULL)
|
||||
{
|
||||
tr_free(data->name);
|
||||
}
|
||||
|
||||
data->name = tr_strdup(name);
|
||||
}
|
||||
|
||||
return data->result;
|
||||
}
|
||||
|
||||
static void reset_callback_data(callback_data* data, tr_watchdir_status result)
|
||||
{
|
||||
tr_free(data->name);
|
||||
|
||||
data->dir = NULL;
|
||||
data->name = NULL;
|
||||
data->result = result;
|
||||
}
|
||||
|
||||
static void create_file(char const* parent_dir, char const* name)
|
||||
{
|
||||
char* const path = tr_buildPath(parent_dir, name, NULL);
|
||||
libtest_create_file_with_string_contents(path, "");
|
||||
tr_free(path);
|
||||
}
|
||||
|
||||
static void create_dir(char const* parent_dir, char const* name)
|
||||
{
|
||||
char* const path = tr_buildPath(parent_dir, name, NULL);
|
||||
tr_sys_dir_create(path, 0, 0700, NULL);
|
||||
tr_free(path);
|
||||
}
|
||||
|
||||
static tr_watchdir_t create_watchdir(char const* path, tr_watchdir_cb callback, void* callback_user_data,
|
||||
struct event_base* event_base)
|
||||
{
|
||||
#ifdef WATCHDIR_TEST_FORCE_GENERIC
|
||||
bool const force_generic = true;
|
||||
#else
|
||||
bool const force_generic = false;
|
||||
#endif
|
||||
|
||||
return tr_watchdir_new(path, callback, callback_user_data, event_base, force_generic);
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static int test_construct(void)
|
||||
{
|
||||
char* const test_dir = libtest_sandbox_create();
|
||||
tr_watchdir_t wd;
|
||||
|
||||
ev_base = event_base_new();
|
||||
|
||||
wd = create_watchdir(test_dir, &callback, NULL, ev_base);
|
||||
check_ptr(wd, !=, NULL);
|
||||
check(tr_sys_path_is_same(test_dir, tr_watchdir_get_path(wd), NULL));
|
||||
|
||||
process_events();
|
||||
|
||||
tr_watchdir_free(wd);
|
||||
|
||||
event_base_free(ev_base);
|
||||
|
||||
libtest_sandbox_destroy(test_dir);
|
||||
tr_free(test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_initial_scan(void)
|
||||
{
|
||||
char* const test_dir = libtest_sandbox_create();
|
||||
|
||||
ev_base = event_base_new();
|
||||
|
||||
/* Speed up generic implementation */
|
||||
tr_watchdir_generic_interval = ONE_HUNDRED_MSEC;
|
||||
|
||||
{
|
||||
callback_data wd_data = CB_DATA_STATIC_INIT;
|
||||
reset_callback_data(&wd_data, TR_WATCHDIR_ACCEPT);
|
||||
|
||||
tr_watchdir_t wd = create_watchdir(test_dir, &callback, &wd_data, ev_base);
|
||||
check_ptr(wd, !=, NULL);
|
||||
|
||||
process_events();
|
||||
check_ptr(wd_data.dir, ==, NULL);
|
||||
check_ptr(wd_data.name, ==, NULL);
|
||||
|
||||
tr_watchdir_free(wd);
|
||||
reset_callback_data(&wd_data, TR_WATCHDIR_ACCEPT);
|
||||
}
|
||||
|
||||
create_file(test_dir, "test");
|
||||
|
||||
{
|
||||
callback_data wd_data = CB_DATA_STATIC_INIT;
|
||||
reset_callback_data(&wd_data, TR_WATCHDIR_ACCEPT);
|
||||
|
||||
tr_watchdir_t wd = create_watchdir(test_dir, &callback, &wd_data, ev_base);
|
||||
check_ptr(wd, !=, NULL);
|
||||
|
||||
process_events();
|
||||
check_ptr(wd_data.dir, ==, wd);
|
||||
check_str(wd_data.name, ==, "test");
|
||||
|
||||
tr_watchdir_free(wd);
|
||||
reset_callback_data(&wd_data, TR_WATCHDIR_ACCEPT);
|
||||
}
|
||||
|
||||
event_base_free(ev_base);
|
||||
|
||||
libtest_sandbox_destroy(test_dir);
|
||||
tr_free(test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_watch(void)
|
||||
{
|
||||
char* const test_dir = libtest_sandbox_create();
|
||||
callback_data wd_data = CB_DATA_STATIC_INIT;
|
||||
tr_watchdir_t wd;
|
||||
|
||||
ev_base = event_base_new();
|
||||
|
||||
/* Speed up generic implementation */
|
||||
tr_watchdir_generic_interval = ONE_HUNDRED_MSEC;
|
||||
|
||||
reset_callback_data(&wd_data, TR_WATCHDIR_ACCEPT);
|
||||
wd = create_watchdir(test_dir, &callback, &wd_data, ev_base);
|
||||
check_ptr(wd, !=, NULL);
|
||||
|
||||
process_events();
|
||||
check_ptr(wd_data.dir, ==, NULL);
|
||||
check_ptr(wd_data.name, ==, NULL);
|
||||
|
||||
create_file(test_dir, "test");
|
||||
|
||||
process_events();
|
||||
check_ptr(wd_data.dir, ==, wd);
|
||||
check_str(wd_data.name, ==, "test");
|
||||
|
||||
reset_callback_data(&wd_data, TR_WATCHDIR_IGNORE);
|
||||
create_file(test_dir, "test2");
|
||||
|
||||
process_events();
|
||||
check_ptr(wd_data.dir, ==, wd);
|
||||
check_str(wd_data.name, ==, "test2");
|
||||
|
||||
reset_callback_data(&wd_data, TR_WATCHDIR_IGNORE);
|
||||
create_dir(test_dir, "test3");
|
||||
|
||||
process_events();
|
||||
check_ptr(wd_data.dir, ==, NULL);
|
||||
check_ptr(wd_data.name, ==, NULL);
|
||||
|
||||
tr_watchdir_free(wd);
|
||||
reset_callback_data(&wd_data, TR_WATCHDIR_ACCEPT);
|
||||
|
||||
event_base_free(ev_base);
|
||||
|
||||
libtest_sandbox_destroy(test_dir);
|
||||
tr_free(test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_watch_two_dirs(void)
|
||||
{
|
||||
char* const test_dir = libtest_sandbox_create();
|
||||
char* const dir1 = tr_buildPath(test_dir, "a", NULL);
|
||||
char* const dir2 = tr_buildPath(test_dir, "b", NULL);
|
||||
callback_data wd1_data = CB_DATA_STATIC_INIT;
|
||||
callback_data wd2_data = CB_DATA_STATIC_INIT;
|
||||
tr_watchdir_t wd1;
|
||||
tr_watchdir_t wd2;
|
||||
|
||||
ev_base = event_base_new();
|
||||
|
||||
/* Speed up generic implementation */
|
||||
tr_watchdir_generic_interval = ONE_HUNDRED_MSEC;
|
||||
|
||||
create_dir(dir1, NULL);
|
||||
create_dir(dir2, NULL);
|
||||
|
||||
reset_callback_data(&wd1_data, TR_WATCHDIR_ACCEPT);
|
||||
wd1 = create_watchdir(dir1, &callback, &wd1_data, ev_base);
|
||||
check_ptr(wd1, !=, NULL);
|
||||
|
||||
reset_callback_data(&wd2_data, TR_WATCHDIR_ACCEPT);
|
||||
wd2 = create_watchdir(dir2, &callback, &wd2_data, ev_base);
|
||||
check_ptr(wd2, !=, NULL);
|
||||
|
||||
process_events();
|
||||
check_ptr(wd1_data.dir, ==, NULL);
|
||||
check_ptr(wd1_data.name, ==, NULL);
|
||||
check_ptr(wd2_data.dir, ==, NULL);
|
||||
check_ptr(wd2_data.name, ==, NULL);
|
||||
|
||||
create_file(dir1, "test");
|
||||
|
||||
process_events();
|
||||
check_ptr(wd1_data.dir, ==, wd1);
|
||||
check_str(wd1_data.name, ==, "test");
|
||||
check_ptr(wd2_data.dir, ==, NULL);
|
||||
check_ptr(wd2_data.name, ==, NULL);
|
||||
|
||||
reset_callback_data(&wd1_data, TR_WATCHDIR_ACCEPT);
|
||||
reset_callback_data(&wd2_data, TR_WATCHDIR_ACCEPT);
|
||||
create_file(dir2, "test2");
|
||||
|
||||
process_events();
|
||||
check_ptr(wd1_data.dir, ==, NULL);
|
||||
check_ptr(wd1_data.name, ==, NULL);
|
||||
check_ptr(wd2_data.dir, ==, wd2);
|
||||
check_str(wd2_data.name, ==, "test2");
|
||||
|
||||
reset_callback_data(&wd1_data, TR_WATCHDIR_IGNORE);
|
||||
reset_callback_data(&wd2_data, TR_WATCHDIR_IGNORE);
|
||||
create_file(dir1, "test3");
|
||||
create_file(dir2, "test4");
|
||||
|
||||
process_events();
|
||||
check_ptr(wd1_data.dir, ==, wd1);
|
||||
check_str(wd1_data.name, ==, "test3");
|
||||
check_ptr(wd2_data.dir, ==, wd2);
|
||||
check_str(wd2_data.name, ==, "test4");
|
||||
|
||||
reset_callback_data(&wd1_data, TR_WATCHDIR_ACCEPT);
|
||||
reset_callback_data(&wd2_data, TR_WATCHDIR_ACCEPT);
|
||||
create_file(dir1, "test5");
|
||||
create_dir(dir2, "test5");
|
||||
|
||||
process_events();
|
||||
check_ptr(wd1_data.dir, ==, wd1);
|
||||
check_str(wd1_data.name, ==, "test5");
|
||||
check_ptr(wd2_data.dir, ==, NULL);
|
||||
check_ptr(wd2_data.name, ==, NULL);
|
||||
|
||||
reset_callback_data(&wd1_data, TR_WATCHDIR_ACCEPT);
|
||||
reset_callback_data(&wd2_data, TR_WATCHDIR_ACCEPT);
|
||||
create_dir(dir1, "test6");
|
||||
create_file(dir2, "test6");
|
||||
|
||||
process_events();
|
||||
check_ptr(wd1_data.dir, ==, NULL);
|
||||
check_ptr(wd1_data.name, ==, NULL);
|
||||
check_ptr(wd2_data.dir, ==, wd2);
|
||||
check_str(wd2_data.name, ==, "test6");
|
||||
|
||||
reset_callback_data(&wd1_data, TR_WATCHDIR_ACCEPT);
|
||||
reset_callback_data(&wd2_data, TR_WATCHDIR_ACCEPT);
|
||||
create_dir(dir1, "test7");
|
||||
create_dir(dir2, "test7");
|
||||
|
||||
process_events();
|
||||
check_ptr(wd1_data.dir, ==, NULL);
|
||||
check_ptr(wd1_data.name, ==, NULL);
|
||||
check_ptr(wd2_data.dir, ==, NULL);
|
||||
check_ptr(wd2_data.name, ==, NULL);
|
||||
|
||||
tr_watchdir_free(wd2);
|
||||
reset_callback_data(&wd2_data, TR_WATCHDIR_ACCEPT);
|
||||
|
||||
tr_watchdir_free(wd1);
|
||||
reset_callback_data(&wd1_data, TR_WATCHDIR_ACCEPT);
|
||||
|
||||
event_base_free(ev_base);
|
||||
|
||||
tr_free(dir2);
|
||||
tr_free(dir1);
|
||||
libtest_sandbox_destroy(test_dir);
|
||||
tr_free(test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_retry(void)
|
||||
{
|
||||
char* const test_dir = libtest_sandbox_create();
|
||||
callback_data wd_data = CB_DATA_STATIC_INIT;
|
||||
tr_watchdir_t wd;
|
||||
|
||||
ev_base = event_base_new();
|
||||
|
||||
/* Speed up generic implementation */
|
||||
tr_watchdir_generic_interval = ONE_HUNDRED_MSEC;
|
||||
|
||||
/* Tune retry logic */
|
||||
tr_watchdir_retry_limit = 10;
|
||||
tr_watchdir_retry_start_interval = FIFTY_MSEC;
|
||||
tr_watchdir_retry_max_interval = tr_watchdir_retry_start_interval;
|
||||
|
||||
reset_callback_data(&wd_data, TR_WATCHDIR_RETRY);
|
||||
wd = create_watchdir(test_dir, &callback, &wd_data, ev_base);
|
||||
check_ptr(wd, !=, NULL);
|
||||
|
||||
process_events();
|
||||
check_ptr(wd_data.dir, ==, NULL);
|
||||
check_ptr(wd_data.name, ==, NULL);
|
||||
|
||||
create_file(test_dir, "test");
|
||||
|
||||
process_events();
|
||||
check_ptr(wd_data.dir, ==, NULL);
|
||||
check_ptr(wd_data.name, ==, NULL);
|
||||
|
||||
reset_callback_data(&wd_data, TR_WATCHDIR_ACCEPT);
|
||||
|
||||
process_events();
|
||||
check_ptr(wd_data.dir, ==, wd);
|
||||
check_str(wd_data.name, ==, "test");
|
||||
|
||||
tr_watchdir_free(wd);
|
||||
reset_callback_data(&wd_data, TR_WATCHDIR_ACCEPT);
|
||||
|
||||
event_base_free(ev_base);
|
||||
|
||||
libtest_sandbox_destroy(test_dir);
|
||||
tr_free(test_dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testFunc const tests[] =
|
||||
{
|
||||
test_construct,
|
||||
test_initial_scan,
|
||||
test_watch,
|
||||
test_watch_two_dirs,
|
||||
test_retry
|
||||
};
|
||||
|
||||
tr_net_init();
|
||||
|
||||
return runTests(tests, NUM_TESTS(tests));
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue