2016-01-02 14:28:59 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* $Id$
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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 }
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
const struct timeval FIFTY_MSEC = { 0, 50000 };
|
|
|
|
const struct timeval ONE_HUNDRED_MSEC = { 0, 100000 };
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-01-25 22:09:49 +00:00
|
|
|
static tr_watchdir_t
|
2016-01-25 21:48:58 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-01-02 14:28:59 +00:00
|
|
|
/***
|
|
|
|
****
|
|
|
|
***/
|
|
|
|
|
|
|
|
static int
|
|
|
|
test_construct (void)
|
|
|
|
{
|
|
|
|
char * const test_dir = libtest_sandbox_create ();
|
|
|
|
tr_watchdir_t wd;
|
|
|
|
|
|
|
|
ev_base = event_base_new();
|
|
|
|
|
2016-01-25 21:48:58 +00:00
|
|
|
wd = create_watchdir (test_dir, &callback, NULL, ev_base);
|
2016-01-02 14:28:59 +00:00
|
|
|
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);
|
|
|
|
|
2016-01-25 21:48:58 +00:00
|
|
|
tr_watchdir_t wd = create_watchdir (test_dir, &callback, &wd_data, ev_base);
|
2016-01-02 14:28:59 +00:00
|
|
|
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);
|
|
|
|
|
2016-01-25 21:48:58 +00:00
|
|
|
tr_watchdir_t wd = create_watchdir (test_dir, &callback, &wd_data, ev_base);
|
2016-01-02 14:28:59 +00:00
|
|
|
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);
|
2016-01-25 21:48:58 +00:00
|
|
|
wd = create_watchdir (test_dir, &callback, &wd_data, ev_base);
|
2016-01-02 14:28:59 +00:00
|
|
|
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);
|
2016-01-25 21:48:58 +00:00
|
|
|
wd1 = create_watchdir (dir1, &callback, &wd1_data, ev_base);
|
2016-01-02 14:28:59 +00:00
|
|
|
check (wd1 != NULL);
|
|
|
|
|
|
|
|
reset_callback_data (&wd2_data, TR_WATCHDIR_ACCEPT);
|
2016-01-25 21:48:58 +00:00
|
|
|
wd2 = create_watchdir (dir2, &callback, &wd2_data, ev_base);
|
2016-01-02 14:28:59 +00:00
|
|
|
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);
|
2016-01-25 21:48:58 +00:00
|
|
|
wd = create_watchdir (test_dir, &callback, &wd_data, ev_base);
|
2016-01-02 14:28:59 +00:00
|
|
|
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));
|
|
|
|
}
|