/* * 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 #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 const struct timeval FIFTY_MSEC = { 0, 50000 }; static const struct timeval ONE_HUNDRED_MSEC = { 0, 100000 }; static const struct timeval TWO_HUNDRED_MSEC = { 0, 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, const char * 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 (const char * parent_dir, const char * name) { char * const path = tr_buildPath (parent_dir, name, NULL); libtest_create_file_with_string_contents (path, ""); tr_free (path); } static void create_dir (const char * parent_dir, const char * 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 (const char * path, tr_watchdir_cb callback, void * callback_user_data, struct event_base * event_base) { #ifdef WATCHDIR_TEST_FORCE_GENERIC const bool force_generic = true; #else const bool 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 (wd != NULL); check (tr_sys_path_is_same (test_dir, tr_watchdir_get_path (wd), NULL)); 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 (wd != NULL); process_events (); check_ptr_eq (NULL, wd_data.dir); check_ptr_eq (NULL, wd_data.name); 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 (wd != NULL); process_events (); check_ptr_eq (wd, wd_data.dir); check_streq ("test", wd_data.name); 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 (wd != NULL); process_events (); check_ptr_eq (NULL, wd_data.dir); check_ptr_eq (NULL, wd_data.name); create_file (test_dir, "test"); process_events (); check_ptr_eq (wd, wd_data.dir); check_streq ("test", wd_data.name); reset_callback_data (&wd_data, TR_WATCHDIR_IGNORE); create_file (test_dir, "test2"); process_events (); check_ptr_eq (wd, wd_data.dir); check_streq ("test2", wd_data.name); reset_callback_data (&wd_data, TR_WATCHDIR_IGNORE); create_dir (test_dir, "test3"); process_events (); check_ptr_eq (NULL, wd_data.dir); check_ptr_eq (NULL, wd_data.name); 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, wd2_data = CB_DATA_STATIC_INIT; tr_watchdir_t wd1, 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 (wd1 != NULL); reset_callback_data (&wd2_data, TR_WATCHDIR_ACCEPT); wd2 = create_watchdir (dir2, &callback, &wd2_data, ev_base); check (wd2 != NULL); process_events (); check_ptr_eq (NULL, wd1_data.dir); check_ptr_eq (NULL, wd1_data.name); check_ptr_eq (NULL, wd2_data.dir); check_ptr_eq (NULL, wd2_data.name); create_file (dir1, "test"); process_events (); check_ptr_eq (wd1, wd1_data.dir); check_streq ("test", wd1_data.name); check_ptr_eq (NULL, wd2_data.dir); check_ptr_eq (NULL, wd2_data.name); reset_callback_data (&wd1_data, TR_WATCHDIR_ACCEPT); reset_callback_data (&wd2_data, TR_WATCHDIR_ACCEPT); create_file (dir2, "test2"); process_events (); check_ptr_eq (NULL, wd1_data.dir); check_ptr_eq (NULL, wd1_data.name); check_ptr_eq (wd2, wd2_data.dir); check_streq ("test2", wd2_data.name); 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_eq (wd1, wd1_data.dir); check_streq ("test3", wd1_data.name); check_ptr_eq (wd2, wd2_data.dir); check_streq ("test4", wd2_data.name); 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_eq (wd1, wd1_data.dir); check_streq ("test5", wd1_data.name); check_ptr_eq (NULL, wd2_data.dir); check_ptr_eq (NULL, wd2_data.name); 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_eq (NULL, wd1_data.dir); check_ptr_eq (NULL, wd1_data.name); check_ptr_eq (wd2, wd2_data.dir); check_streq ("test6", wd2_data.name); 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_eq (NULL, wd1_data.dir); check_ptr_eq (NULL, wd1_data.name); check_ptr_eq (NULL, wd2_data.dir); check_ptr_eq (NULL, wd2_data.name); 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 (wd != NULL); process_events (); check_ptr_eq (NULL, wd_data.dir); check_ptr_eq (NULL, wd_data.name); create_file (test_dir, "test"); process_events (); check_ptr_eq (NULL, wd_data.dir); check_ptr_eq (NULL, wd_data.name); reset_callback_data (&wd_data, TR_WATCHDIR_ACCEPT); process_events (); check_ptr_eq (wd, wd_data.dir); check_streq ("test", wd_data.name); 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) { const testFunc tests[] = { test_construct, test_initial_scan, test_watch, test_watch_two_dirs, test_retry }; tr_net_init (); return runTests (tests, NUM_TESTS (tests)); }