From b6a4741a15371882a477dbc9a684faf6b39ec000 Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Sun, 1 Nov 2026 01:26:05 +0100 Subject: Add tests for the ot_mutex code, it didn't have any until now --- Makefile | 2 +- tests/runner.c | 1 + tests/t_mutex.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test.h | 1 + 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 tests/t_mutex.c diff --git a/Makefile b/Makefile index c9aeafd..0e5e985 100644 --- a/Makefile +++ b/Makefile @@ -96,7 +96,7 @@ proxy.debug: $(SOURCES_proxy) $(HEADERS) # has to. tests/stubs.c supplies the few symbols those modules reference but # the tests never reach. TESTBIN=tests/runner -TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_urlencoded.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c tests/t_socket.c tests/t_workqueue.c tests/t_batch.c tests/t_fdstate.c tests/t_event.c tests/t_fmt.c +TEST_SOURCES=tests/runner.c tests/test.c tests/stubs.c tests/t_urlencoded.c tests/t_iovec.c tests/t_vector.c tests/t_random.c tests/t_hex.c tests/t_accesslist.c tests/t_scan.c tests/t_buf.c tests/t_ip.c tests/t_socket.c tests/t_mutex.c tests/t_workqueue.c tests/t_batch.c tests/t_fdstate.c tests/t_event.c tests/t_fmt.c TEST_UNITS=ot_urlencoded.c ot_fmt.c ot_event.c ot_batch.c ot_fdstate.c ot_buf.c ot_ip.c ot_socket.c ot_iovec.c ot_vector.c ot_random.c ot_hex.c ot_scan.c ot_accesslist.c ot_mutex.c ot_workqueue.c TEST_HEADERS=tests/test.h $(HEADERS) # Tests reach code that sits behind feature flags, so the units under test are diff --git a/tests/runner.c b/tests/runner.c index 38bfb86..dcb009c 100644 --- a/tests/runner.c +++ b/tests/runner.c @@ -27,6 +27,7 @@ static const struct module modules[] = {{"ot_urlencoded", test_urlencoded}, {"ot_buf", test_buf}, {"ot_ip", test_ip}, {"ot_socket", test_socket}, + {"ot_mutex", test_mutex}, {"ot_workqueue", test_workqueue}, {"ot_batch", test_batch}, {"ot_fdstate", test_fdstate}, diff --git a/tests/t_mutex.c b/tests/t_mutex.c new file mode 100644 index 0000000..7c1463b --- /dev/null +++ b/tests/t_mutex.c @@ -0,0 +1,138 @@ +/* This software was written by Dirk Engling + It is considered beerware. Prost. Skol. Cheers or whatever. + + $id$ */ + +/* System */ +#include +#include +#include +#include + +/* Opentracker */ +#include "ot_mutex.h" +#include "test.h" + +/* Every announce passes through here, and what it has to get right is which + bucket a hash belongs to and that two threads never hold one bucket at + once. Both are checked against real mutexes rather than a stand-in. */ + +/* A hash that lands in a chosen bucket: the bucket is the top + OT_BUCKET_COUNT_BITS of the first four bytes, read in network order */ +static void hash_for_bucket(ot_hash hash, int bucket) { + memset(hash, 0, sizeof(ot_hash)); + hash[0] = (uint8_t)(bucket >> (OT_BUCKET_COUNT_BITS - 8)); + hash[1] = (uint8_t)((bucket << (16 - OT_BUCKET_COUNT_BITS)) & 0xff); +} + +static void case_a_hash_names_its_bucket(void) { + static const int buckets[] = {0, 1, 2, 17, 511, 512, OT_BUCKET_COUNT - 1}; + size_t i; + + test_case("a hash is served by the bucket its leading bits name"); + for (i = 0; i < sizeof(buckets) / sizeof(buckets[0]); ++i) { + ot_hash hash; + ot_vector *by_hash, *by_number; + + hash_for_bucket(hash, buckets[i]); + by_hash = mutex_bucket_lock_by_hash(hash); + mutex_bucket_unlock_by_hash(hash, 0); + + by_number = mutex_bucket_lock(buckets[i]); + mutex_bucket_unlock(buckets[i], 0); + + CHECK(by_hash == by_number); + } +} + +static void case_every_bucket_is_its_own(void) { + ot_vector *first, *last, *middle; + + test_case("no two buckets are the same one"); + first = mutex_bucket_lock(0); + mutex_bucket_unlock(0, 0); + middle = mutex_bucket_lock(OT_BUCKET_COUNT / 2); + mutex_bucket_unlock(OT_BUCKET_COUNT / 2, 0); + last = mutex_bucket_lock(OT_BUCKET_COUNT - 1); + mutex_bucket_unlock(OT_BUCKET_COUNT - 1, 0); + + CHECK(first != middle); + CHECK(middle != last); + CHECK(first != last); +} + +static void case_the_count_follows_what_unlock_is_told(void) { + size_t before = mutex_get_torrent_count(); + + test_case("the torrent count moves by what unlocking reports, and not otherwise"); + mutex_bucket_lock(3); + mutex_bucket_unlock(3, 0); + CHECK_INT((int)(mutex_get_torrent_count() - before), 0); + + mutex_bucket_lock(3); + mutex_bucket_unlock(3, 5); + CHECK_INT((int)(mutex_get_torrent_count() - before), 5); + + mutex_bucket_lock(3); + mutex_bucket_unlock(3, -2); + CHECK_INT((int)(mutex_get_torrent_count() - before), 3); +} + +static _Atomic int g_other_got_in; + +static void *take_a_bucket(void *args) { + int bucket = (int)(intptr_t)args; + + mutex_bucket_lock(bucket); + atomic_store(&g_other_got_in, 1); + mutex_bucket_unlock(bucket, 0); + return NULL; +} + +static int waited_for_entry(int patience_ms) { + int waited; + + for (waited = 0; waited < patience_ms && !atomic_load(&g_other_got_in); ++waited) + usleep(1000); + return atomic_load(&g_other_got_in); +} + +static void case_a_held_bucket_keeps_others_out(void) { + pthread_t other; + + test_case("a second thread waits for the bucket this one holds"); + atomic_store(&g_other_got_in, 0); + mutex_bucket_lock(9); + CHECK_INT(pthread_create(&other, NULL, take_a_bucket, (void *)(intptr_t)9), 0); + + CHECK_INT(waited_for_entry(200), 0); + + mutex_bucket_unlock(9, 0); + CHECK_INT(waited_for_entry(2000), 1); + pthread_join(other, NULL); +} + +static void case_another_bucket_is_free_meanwhile(void) { + pthread_t other; + + test_case("and a different bucket is not held up by it"); + atomic_store(&g_other_got_in, 0); + mutex_bucket_lock(9); + CHECK_INT(pthread_create(&other, NULL, take_a_bucket, (void *)(intptr_t)10), 0); + + CHECK_INT(waited_for_entry(2000), 1); + pthread_join(other, NULL); + mutex_bucket_unlock(9, 0); +} + +void test_mutex(void) { + mutex_init(); + + case_a_hash_names_its_bucket(); + case_every_bucket_is_its_own(); + case_the_count_follows_what_unlock_is_told(); + case_a_held_bucket_keeps_others_out(); + case_another_bucket_is_free_meanwhile(); + + mutex_deinit(); +} diff --git a/tests/test.h b/tests/test.h index d58742c..7582dab 100644 --- a/tests/test.h +++ b/tests/test.h @@ -86,6 +86,7 @@ void test_scan(void); void test_buf(void); void test_ip(void); void test_socket(void); +void test_mutex(void); void test_workqueue(void); void test_batch(void); void test_fdstate(void); -- cgit v1.3