Commit 7a630052 authored by Nick Mathewson's avatar Nick Mathewson 🐻
Browse files

Basic unit test for condition variables.

parent e47a90a9
......@@ -437,7 +437,7 @@ AC_CHECK_FUNCS(
sysconf \
sysctl \
uname \
usleep \
usleep \
vasprintf \
_vscprintf
)
......
......@@ -1258,6 +1258,24 @@ test_stats(void *arg)
tor_free(s);
}
static void *
passthrough_test_setup(const struct testcase_t *testcase)
{
return testcase->setup_data;
}
static int
passthrough_test_cleanup(const struct testcase_t *testcase, void *ptr)
{
(void)testcase;
(void)ptr;
return 1;
}
const struct testcase_setup_t passthrough_setup = {
passthrough_test_setup, passthrough_test_cleanup
};
#define ENT(name) \
{ #name, test_ ## name , 0, NULL, NULL }
#define FORK(name) \
......
......@@ -158,5 +158,7 @@ crypto_pk_t *pk_generate(int idx);
#define NS_MOCK(name) MOCK(name, NS(name))
#define NS_UNMOCK(name) UNMOCK(name)
extern const struct testcase_setup_t passthrough_setup;
#endif
......@@ -1975,30 +1975,14 @@ test_crypto_siphash(void *arg)
;
}
static void *
pass_data_setup_fn(const struct testcase_t *testcase)
{
return testcase->setup_data;
}
static int
pass_data_cleanup_fn(const struct testcase_t *testcase, void *ptr)
{
(void)ptr;
(void)testcase;
return 1;
}
static const struct testcase_setup_t pass_data = {
pass_data_setup_fn, pass_data_cleanup_fn
};
#define CRYPTO_LEGACY(name) \
{ #name, test_crypto_ ## name , 0, NULL, NULL }
struct testcase_t crypto_tests[] = {
CRYPTO_LEGACY(formats),
CRYPTO_LEGACY(rng),
{ "aes_AES", test_crypto_aes, TT_FORK, &pass_data, (void*)"aes" },
{ "aes_EVP", test_crypto_aes, TT_FORK, &pass_data, (void*)"evp" },
{ "aes_AES", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"aes" },
{ "aes_EVP", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"evp" },
CRYPTO_LEGACY(sha),
CRYPTO_LEGACY(pk),
{ "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL },
......@@ -2006,23 +1990,25 @@ struct testcase_t crypto_tests[] = {
CRYPTO_LEGACY(dh),
CRYPTO_LEGACY(s2k_rfc2440),
#ifdef HAVE_LIBSCRYPT_H
{ "s2k_scrypt", test_crypto_s2k_general, 0, &pass_data,
{ "s2k_scrypt", test_crypto_s2k_general, 0, &passthrough_setup,
(void*)"scrypt" },
{ "s2k_scrypt_low", test_crypto_s2k_general, 0, &pass_data,
{ "s2k_scrypt_low", test_crypto_s2k_general, 0, &passthrough_setup,
(void*)"scrypt-low" },
#endif
{ "s2k_pbkdf2", test_crypto_s2k_general, 0, &pass_data,
{ "s2k_pbkdf2", test_crypto_s2k_general, 0, &passthrough_setup,
(void*)"pbkdf2" },
{ "s2k_rfc2440_general", test_crypto_s2k_general, 0, &pass_data,
{ "s2k_rfc2440_general", test_crypto_s2k_general, 0, &passthrough_setup,
(void*)"rfc2440" },
{ "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &pass_data,
{ "s2k_rfc2440_legacy", test_crypto_s2k_general, 0, &passthrough_setup,
(void*)"rfc2440-legacy" },
{ "s2k_errors", test_crypto_s2k_errors, 0, NULL, NULL },
{ "scrypt_vectors", test_crypto_scrypt_vectors, 0, NULL, NULL },
{ "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL },
{ "pwbox", test_crypto_pwbox, 0, NULL, NULL },
{ "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"aes" },
{ "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &pass_data, (void*)"evp" },
{ "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &passthrough_setup,
(void*)"aes" },
{ "aes_iv_EVP", test_crypto_aes_iv, TT_FORK, &passthrough_setup,
(void*)"evp" },
CRYPTO_LEGACY(base32_decode),
{ "kdf_TAP", test_crypto_kdf_TAP, 0, NULL, NULL },
{ "hkdf_sha256", test_crypto_hkdf_sha256, 0, NULL, NULL },
......
......@@ -144,11 +144,159 @@ test_threads_basic(void *arg)
tor_mutex_free(thread_test_start2_);
}
#define THREAD_TEST(name) \
typedef struct cv_testinfo_s {
tor_cond_t *cond;
tor_mutex_t *mutex;
int value;
int addend;
int shutdown;
int n_shutdown;
int n_wakeups;
int n_timeouts;
int n_threads;
const struct timeval *tv;
} cv_testinfo_t;
static cv_testinfo_t *
cv_testinfo_new(void)
{
cv_testinfo_t *i = tor_malloc_zero(sizeof(*i));
i->cond = tor_cond_new();
i->mutex = tor_mutex_new_nonrecursive();
return i;
}
static void
cv_testinfo_free(cv_testinfo_t *i)
{
if (!i)
return;
tor_cond_free(i->cond);
tor_mutex_free(i->mutex);
tor_free(i);
}
static void cv_test_thr_fn_(void *arg) ATTR_NORETURN;
static void
cv_test_thr_fn_(void *arg)
{
cv_testinfo_t *i = arg;
int tid, r;
tor_mutex_acquire(i->mutex);
tid = i->n_threads++;
tor_mutex_release(i->mutex);
(void) tid;
tor_mutex_acquire(i->mutex);
while (1) {
if (i->addend) {
i->value += i->addend;
i->addend = 0;
}
if (i->shutdown) {
++i->n_shutdown;
i->shutdown = 0;
tor_mutex_release(i->mutex);
spawn_exit();
}
r = tor_cond_wait(i->cond, i->mutex, i->tv);
++i->n_wakeups;
if (r == 1) {
++i->n_timeouts;
tor_mutex_release(i->mutex);
spawn_exit();
}
}
}
static void
test_threads_conditionvar(void *arg)
{
cv_testinfo_t *ti=NULL;
const struct timeval msec100 = { 0, 100*1000 };
const int timeout = !strcmp(arg, "tv");
ti = cv_testinfo_new();
if (timeout) {
ti->tv = &msec100;
}
spawn_func(cv_test_thr_fn_, ti);
spawn_func(cv_test_thr_fn_, ti);
spawn_func(cv_test_thr_fn_, ti);
spawn_func(cv_test_thr_fn_, ti);
tor_mutex_acquire(ti->mutex);
ti->addend = 7;
ti->shutdown = 1;
tor_cond_signal_one(ti->cond);
tor_mutex_release(ti->mutex);
#define SPIN() \
while (1) { \
tor_mutex_acquire(ti->mutex); \
if (ti->addend == 0) { \
break; \
} \
tor_mutex_release(ti->mutex); \
}
SPIN();
ti->addend = 30;
ti->shutdown = 1;
tor_cond_signal_all(ti->cond);
tor_mutex_release(ti->mutex);
SPIN();
ti->addend = 1000;
if (! timeout) ti->shutdown = 1;
tor_cond_signal_one(ti->cond);
tor_mutex_release(ti->mutex);
SPIN();
ti->addend = 300;
if (! timeout) ti->shutdown = 1;
tor_cond_signal_all(ti->cond);
tor_mutex_release(ti->mutex);
SPIN();
tor_mutex_release(ti->mutex);
tt_int_op(ti->value, ==, 1337);
if (!timeout) {
tt_int_op(ti->n_shutdown, ==, 4);
} else {
#ifdef _WIN32
Sleep(500); /* msec */
#elif defined(HAVE_USLEEP)
usleep(500*1000); /* usec */
#else
{
struct tv = { 0, 500*1000 };
select(0, NULL, NULL, NULL, &tv);
}
#endif
tor_mutex_acquire(ti->mutex);
tt_int_op(ti->n_shutdown, ==, 2);
tt_int_op(ti->n_timeouts, ==, 2);
tor_mutex_release(ti->mutex);
}
done:
cv_testinfo_free(ti);
}
#define THREAD_TEST(name) \
{ #name, test_threads_##name, TT_FORK, NULL, NULL }
struct testcase_t thread_tests[] = {
THREAD_TEST(basic),
{ "conditionvar", test_threads_conditionvar, TT_FORK,
&passthrough_setup, (void*)"no-tv" },
{ "conditionvar_timeout", test_threads_conditionvar, TT_FORK,
&passthrough_setup, (void*)"tv" },
END_OF_TESTCASES
};
......@@ -4646,23 +4646,6 @@ test_util_socket(void *arg)
tor_close_socket(fd4);
}
static void *
socketpair_test_setup(const struct testcase_t *testcase)
{
return testcase->setup_data;
}
static int
socketpair_test_cleanup(const struct testcase_t *testcase, void *ptr)
{
(void)testcase;
(void)ptr;
return 1;
}
static const struct testcase_setup_t socketpair_setup = {
socketpair_test_setup, socketpair_test_cleanup
};
/* Test for socketpair and ersatz_socketpair(). We test them both, since
* the latter is a tolerably good way to exersize tor_accept_socket(). */
static void
......@@ -4837,10 +4820,10 @@ struct testcase_t util_tests[] = {
UTIL_TEST(mathlog, 0),
UTIL_TEST(weak_random, 0),
UTIL_TEST(socket, TT_FORK),
{ "socketpair", test_util_socketpair, TT_FORK, &socketpair_setup,
{ "socketpair", test_util_socketpair, TT_FORK, &passthrough_setup,
(void*)"0" },
{ "socketpair_ersatz", test_util_socketpair, TT_FORK,
&socketpair_setup, (void*)"1" },
&passthrough_setup, (void*)"1" },
UTIL_TEST(max_mem, 0),
UTIL_TEST(hostname_validation, 0),
UTIL_TEST(ipv4_validation, 0),
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment