diff options
Diffstat (limited to 'src/lib/ssm/tests/rbuff_test.c')
| -rw-r--r-- | src/lib/ssm/tests/rbuff_test.c | 488 |
1 files changed, 448 insertions, 40 deletions
diff --git a/src/lib/ssm/tests/rbuff_test.c b/src/lib/ssm/tests/rbuff_test.c index 58cb39c3..b7ef3dfb 100644 --- a/src/lib/ssm/tests/rbuff_test.c +++ b/src/lib/ssm/tests/rbuff_test.c @@ -34,6 +34,10 @@ #include <ouroboros/errno.h> #include <ouroboros/time.h> +/* Mirrors TXQ_MIN_SLOTS in ssm/rbuff.c; keep in sync. */ +#define FLOOR_SLOTS 4 +#define CEIL_SLOTS (SSM_RBUFF_SIZE - 1 - SSM_RBUFF_TXQ_RESERVE) + #include <errno.h> #include <stdio.h> #include <unistd.h> @@ -54,6 +58,7 @@ static int test_ssm_rbuff_create_destroy(void) ssm_rbuff_destroy(rb); TEST_SUCCESS(); + return TEST_RC_SUCCESS; fail: @@ -100,6 +105,7 @@ static int test_ssm_rbuff_write_read(void) ssm_rbuff_destroy(rb); TEST_SUCCESS(); + return TEST_RC_SUCCESS; fail_rb: @@ -131,6 +137,7 @@ static int test_ssm_rbuff_read_empty(void) ssm_rbuff_destroy(rb); TEST_SUCCESS(); + return TEST_RC_SUCCESS; fail_rb: @@ -160,6 +167,7 @@ static int test_ssm_rbuff_fill_drain(void) i, ssm_rbuff_queued(rb)); goto fail_rb; } + if (ssm_rbuff_write(rb, i) < 0) { printf("Failed to write at index %zu.\n", i); goto fail_rb; @@ -195,21 +203,23 @@ static int test_ssm_rbuff_fill_drain(void) ssm_rbuff_destroy(rb); TEST_SUCCESS(); + return TEST_RC_SUCCESS; fail_rb: while (ssm_rbuff_read(rb) >= 0) ; + ssm_rbuff_destroy(rb); fail: TEST_FAIL(); return TEST_RC_FAIL; } -static int test_ssm_rbuff_acl(void) +static int test_ssm_rbuff_flags(void) { struct ssm_rbuff * rb; - uint32_t acl; + uint32_t flags; TEST_START(); @@ -219,16 +229,17 @@ static int test_ssm_rbuff_acl(void) goto fail; } - acl = ssm_rbuff_get_acl(rb); - if (acl != ACL_RDWR) { - printf("Expected ACL_RDWR, got %u.\n", acl); + flags = ssm_rbuff_get_flags(rb); + if (flags != RB_RDWR) { + printf("Expected RB_RDWR, got %u.\n", flags); goto fail_rb; } - ssm_rbuff_set_acl(rb, ACL_RDONLY); - acl = ssm_rbuff_get_acl(rb); - if (acl != ACL_RDONLY) { - printf("Expected ACL_RDONLY, got %u.\n", acl); + ssm_rbuff_clr_flags(rb, RB_WR); + + flags = ssm_rbuff_get_flags(rb); + if (flags != RB_RD) { + printf("Expected RB_RD, got %u.\n", flags); goto fail_rb; } @@ -237,7 +248,8 @@ static int test_ssm_rbuff_acl(void) goto fail_rb; } - ssm_rbuff_set_acl(rb, ACL_FLOWDOWN); + ssm_rbuff_set_flags(rb, RB_FLOWDOWN); + if (ssm_rbuff_write(rb, 1) != -EFLOWDOWN) { printf("Expected -EFLOWDOWN on FLOWDOWN.\n"); goto fail_rb; @@ -251,6 +263,7 @@ static int test_ssm_rbuff_acl(void) ssm_rbuff_destroy(rb); TEST_SUCCESS(); + return TEST_RC_SUCCESS; fail_rb: @@ -302,6 +315,7 @@ static int test_ssm_rbuff_open_close(void) ssm_rbuff_destroy(rb1); TEST_SUCCESS(); + return TEST_RC_SUCCESS; fail_rb2: @@ -348,8 +362,10 @@ static void * reader_thread(void * arg) val = ssm_rbuff_read(args->rb); while (val < 0) { nanosleep(&delay, NULL); + val = ssm_rbuff_read(args->rb); } + if (val != i) { printf("Expected %d, got %zd.\n", i, val); return (void *) -1; @@ -359,7 +375,7 @@ static void * reader_thread(void * arg) return NULL; } -static void * blocking_writer_thread(void * arg) +static void * blocking_wr_thread(void * arg) { struct thread_args * args = (struct thread_args *) arg; int i; @@ -372,7 +388,7 @@ static void * blocking_writer_thread(void * arg) return NULL; } -static void * blocking_reader_thread(void * arg) +static void * blocking_rd_thread(void * arg) { struct thread_args * args = (struct thread_args *) arg; int i; @@ -391,13 +407,13 @@ static void * blocking_reader_thread(void * arg) static int test_ssm_rbuff_blocking(void) { - struct ssm_rbuff * rb; - pthread_t wthread; - pthread_t rthread; - struct thread_args args; - struct timespec delay = {0, 10 * MILLION}; - void * ret_w; - void * ret_r; + struct ssm_rbuff * rb; + pthread_t wthread; + pthread_t rthread; + struct thread_args args; + struct timespec delay = {0, 10 * MILLION}; + void * ret_w; + void * ret_r; TEST_START(); @@ -410,15 +426,14 @@ static int test_ssm_rbuff_blocking(void) args.rb = rb; args.iterations = 50; args.delay_us = 0; - - if (pthread_create(&rthread, NULL, blocking_reader_thread, &args)) { + if (pthread_create(&rthread, NULL, blocking_rd_thread, &args) != 0) { printf("Failed to create reader thread.\n"); goto fail_rthread; } nanosleep(&delay, NULL); - if (pthread_create(&wthread, NULL, blocking_writer_thread, &args)) { + if (pthread_create(&wthread, NULL, blocking_wr_thread, &args) != 0) { printf("Failed to create writer thread.\n"); pthread_cancel(rthread); goto fail_wthread; @@ -435,6 +450,7 @@ static int test_ssm_rbuff_blocking(void) ssm_rbuff_destroy(rb); TEST_SUCCESS(); + return TEST_RC_SUCCESS; fail_ret: @@ -482,8 +498,7 @@ static int test_ssm_rbuff_blocking_timeout(void) (end.tv_nsec - start.tv_nsec) / 1000000L; if (elapsed_ms < 90 || elapsed_ms > 200) { - printf("Timeout took %ld ms, expected ~100 ms.\n", - elapsed_ms); + printf("Timeout took %ld ms, expected ~100 ms.\n", elapsed_ms); goto fail_rb; } @@ -502,8 +517,7 @@ static int test_ssm_rbuff_blocking_timeout(void) clock_gettime(PTHREAD_COND_CLOCK, &end); if (ret != -ETIMEDOUT) { - printf("Expected -ETIMEDOUT on full buffer, got %zd.\n", - ret); + printf("Expected -ETIMEDOUT on full buffer, got %zd.\n", ret); goto fail_rb; } @@ -522,11 +536,13 @@ static int test_ssm_rbuff_blocking_timeout(void) ssm_rbuff_destroy(rb); TEST_SUCCESS(); + return TEST_RC_SUCCESS; fail_rb: while (ssm_rbuff_read(rb) >= 0) ; + ssm_rbuff_destroy(rb); fail: TEST_FAIL(); @@ -553,7 +569,7 @@ static int test_ssm_rbuff_blocking_flowdown(void) clock_gettime(PTHREAD_COND_CLOCK, &now); ts_add(&now, &interval, &abs_timeout); - ssm_rbuff_set_acl(rb, ACL_FLOWDOWN); + ssm_rbuff_set_flags(rb, RB_FLOWDOWN); ret = ssm_rbuff_read_b(rb, &abs_timeout); if (ret != -EFLOWDOWN) { @@ -561,7 +577,7 @@ static int test_ssm_rbuff_blocking_flowdown(void) goto fail_rb; } - ssm_rbuff_set_acl(rb, ACL_RDWR); + ssm_rbuff_clr_flags(rb, RB_FLOWDOWN); for (i = 0; i < SSM_RBUFF_SIZE - 1; ++i) { if (ssm_rbuff_write(rb, i) < 0) { @@ -573,7 +589,7 @@ static int test_ssm_rbuff_blocking_flowdown(void) clock_gettime(PTHREAD_COND_CLOCK, &now); ts_add(&now, &interval, &abs_timeout); - ssm_rbuff_set_acl(rb, ACL_FLOWDOWN); + ssm_rbuff_set_flags(rb, RB_FLOWDOWN); ret = ssm_rbuff_write_b(rb, 999, &abs_timeout); if (ret != -EFLOWDOWN) { @@ -581,18 +597,21 @@ static int test_ssm_rbuff_blocking_flowdown(void) goto fail_rb; } - ssm_rbuff_set_acl(rb, ACL_RDWR); + ssm_rbuff_clr_flags(rb, RB_FLOWDOWN); + while (ssm_rbuff_read(rb) >= 0) ; ssm_rbuff_destroy(rb); TEST_SUCCESS(); + return TEST_RC_SUCCESS; fail_rb: while (ssm_rbuff_read(rb) >= 0) ; + ssm_rbuff_destroy(rb); fail: TEST_FAIL(); @@ -601,12 +620,12 @@ static int test_ssm_rbuff_blocking_flowdown(void) static int test_ssm_rbuff_threaded(void) { - struct ssm_rbuff * rb; - pthread_t wthread; - pthread_t rthread; - struct thread_args args; - void * ret_w; - void * ret_r; + struct ssm_rbuff * rb; + pthread_t wthread; + pthread_t rthread; + struct thread_args args; + void * ret_w; + void * ret_r; TEST_START(); @@ -619,13 +638,12 @@ static int test_ssm_rbuff_threaded(void) args.rb = rb; args.iterations = 100; args.delay_us = 100; - - if (pthread_create(&wthread, NULL, writer_thread, &args)) { + if (pthread_create(&wthread, NULL, writer_thread, &args) != 0) { printf("Failed to create writer thread.\n"); goto fail_rb; } - if (pthread_create(&rthread, NULL, reader_thread, &args)) { + if (pthread_create(&rthread, NULL, reader_thread, &args) != 0) { printf("Failed to create reader thread.\n"); pthread_cancel(wthread); pthread_join(wthread, NULL); @@ -643,9 +661,393 @@ static int test_ssm_rbuff_threaded(void) ssm_rbuff_destroy(rb); TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_rb: + ssm_rbuff_destroy(rb); + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_ssm_rbuff_limit_off(void) +{ + struct ssm_rbuff * rb; + size_t i; + + TEST_START(); + + rb = ssm_rbuff_create(getpid(), 11); + if (rb == NULL) { + printf("Failed to create rbuff.\n"); + goto fail; + } + + if (ssm_rbuff_get_limit(rb) != SSM_RBUFF_SIZE - 1) { + printf("Expected default limit %d, got %zu.\n", + SSM_RBUFF_SIZE - 1, ssm_rbuff_get_limit(rb)); + goto fail_rb; + } + + for (i = 0; i < SSM_RBUFF_SIZE - 1; ++i) { + if (ssm_rbuff_write(rb, i) < 0) { + printf("Failed to write at index %zu.\n", i); + goto fail_rb; + } + } + + if (ssm_rbuff_write(rb, 999) != -EAGAIN) { + printf("Expected -EAGAIN on physically full buffer.\n"); + goto fail_rb; + } + + while (ssm_rbuff_read(rb) >= 0) + ; + + ssm_rbuff_destroy(rb); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_rb: + while (ssm_rbuff_read(rb) >= 0) + ; + + ssm_rbuff_destroy(rb); + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_ssm_rbuff_limit_slow(void) +{ + struct ssm_rbuff * rb; + struct timespec dfl = TIMESPEC_INIT_MS(SSM_RBUFF_TXQ_DELAY); + struct timespec delay = {0, 10 * MILLION}; + size_t limit; + size_t i; + + TEST_START(); + + rb = ssm_rbuff_create(getpid(), 12); + if (rb == NULL) { + printf("Failed to create rbuff.\n"); + goto fail; + } + + ssm_rbuff_set_txq_target(rb, &dfl); + + for (i = 0; i < 32; ++i) { + if (ssm_rbuff_write_b(rb, i, NULL) < 0) { + printf("Failed to write at index %zu.\n", i); + goto fail_rb; + } + nanosleep(&delay, NULL); + + if (ssm_rbuff_read(rb) < 0) { + printf("Failed to read at index %zu.\n", i); + goto fail_rb; + } + } + + limit = ssm_rbuff_get_limit(rb); + if (limit > FLOOR_SLOTS) { + printf("Expected limit near the floor, got %zu.\n", limit); + goto fail_rb; + } + + ssm_rbuff_destroy(rb); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_rb: + while (ssm_rbuff_read(rb) >= 0) + ; + + ssm_rbuff_destroy(rb); + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_ssm_rbuff_limit_fast(void) +{ + struct ssm_rbuff * rb; + struct timespec dfl = TIMESPEC_INIT_MS(SSM_RBUFF_TXQ_DELAY); + size_t limit; + size_t i; + + TEST_START(); + + rb = ssm_rbuff_create(getpid(), 13); + if (rb == NULL) { + printf("Failed to create rbuff.\n"); + goto fail; + } + + ssm_rbuff_set_txq_target(rb, &dfl); + + for (i = 0; i < 200; ++i) { + if (ssm_rbuff_write_b(rb, i, NULL) < 0) { + printf("Failed to write at index %zu.\n", i); + goto fail_rb; + } + + if (ssm_rbuff_read(rb) < 0) { + printf("Failed to read at index %zu.\n", i); + goto fail_rb; + } + } + + limit = ssm_rbuff_get_limit(rb); + if (limit != CEIL_SLOTS) { + printf("Expected limit %d, got %zu.\n", CEIL_SLOTS, limit); + goto fail_rb; + } + + ssm_rbuff_destroy(rb); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_rb: + while (ssm_rbuff_read(rb) >= 0) + ; + + ssm_rbuff_destroy(rb); + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_ssm_rbuff_limit_floor(void) +{ + struct ssm_rbuff * rb; + struct timespec dfl = TIMESPEC_INIT_MS(SSM_RBUFF_TXQ_DELAY); + struct timespec interval = {0, 50 * MILLION}; + struct timespec now; + struct timespec abs_timeout; + size_t limit; + int ret = 0; + size_t i; + + TEST_START(); + + rb = ssm_rbuff_create(getpid(), 14); + if (rb == NULL) { + printf("Failed to create rbuff.\n"); + goto fail; + } + + ssm_rbuff_set_txq_target(rb, &dfl); + + clock_gettime(PTHREAD_COND_CLOCK, &now); + ts_add(&now, &interval, &abs_timeout); + + for (i = 0; i < SSM_RBUFF_SIZE; ++i) { + ret = ssm_rbuff_write_b(rb, i, &abs_timeout); + if (ret == -ETIMEDOUT) + break; + + if (ret < 0) { + printf("Write failed at index %zu: %d.\n", i, ret); + goto fail_rb; + } + } + + if (ret != -ETIMEDOUT) { + printf("Expected the limiter to block the ring.\n"); + goto fail_rb; + } + + limit = ssm_rbuff_get_limit(rb); + if (limit > FLOOR_SLOTS) { + printf("Expected floor limit, got %zu.\n", limit); + goto fail_rb; + } + + while (ssm_rbuff_read(rb) >= 0) + ; + + ssm_rbuff_destroy(rb); + + TEST_SUCCESS(); + return TEST_RC_SUCCESS; fail_rb: + while (ssm_rbuff_read(rb) >= 0) + ; + + ssm_rbuff_destroy(rb); + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +/* A fresh ring is unlimited; rx rings must not inherit a bound. */ +static int test_ssm_rbuff_txq_target(void) +{ + struct ssm_rbuff * rb; + struct timespec dfl = TIMESPEC_INIT_MS(SSM_RBUFF_TXQ_DELAY); + struct timespec delay = {0, 5 * MILLION}; + struct timespec small = {0, 2 * MILLION}; + struct timespec big = {0, 200 * MILLION}; + struct timespec def; + struct timespec got; + size_t limit_small; + size_t limit_big; + size_t i; + + TEST_START(); + + rb = ssm_rbuff_create(getpid(), 15); + if (rb == NULL) { + printf("Failed to create rbuff.\n"); + goto fail; + } + + ssm_rbuff_get_txq_target(rb, &got); + + if (got.tv_sec != 0 || got.tv_nsec != 0) { + printf("A new ring is not unlimited.\n"); + goto fail_rb; + } + + ssm_rbuff_set_txq_target(rb, &dfl); + ssm_rbuff_get_txq_target(rb, &def); + + ssm_rbuff_set_txq_target(rb, &small); + + for (i = 0; i < 64; ++i) { + if (ssm_rbuff_write_b(rb, i, NULL) < 0) { + printf("Failed to write at index %zu.\n", i); + goto fail_rb; + } + nanosleep(&delay, NULL); + + if (ssm_rbuff_read(rb) < 0) { + printf("Failed to read at index %zu.\n", i); + goto fail_rb; + } + } + + limit_small = ssm_rbuff_get_limit(rb); + + ssm_rbuff_set_txq_target(rb, &big); + + for (i = 0; i < 64; ++i) { + if (ssm_rbuff_write_b(rb, i, NULL) < 0) { + printf("Failed to write at index %zu.\n", i); + goto fail_rb; + } + nanosleep(&delay, NULL); + + if (ssm_rbuff_read(rb) < 0) { + printf("Failed to read at index %zu.\n", i); + goto fail_rb; + } + } + + limit_big = ssm_rbuff_get_limit(rb); + if (limit_big <= limit_small) { + printf("Expected a larger target to grow the limit: " + "%zu -> %zu.\n", limit_small, limit_big); + goto fail_rb; + } + + ssm_rbuff_set_txq_target(rb, &dfl); + ssm_rbuff_get_txq_target(rb, &got); + + if (got.tv_sec != def.tv_sec || got.tv_nsec != def.tv_nsec) { + printf("NULL did not restore the default target.\n"); + goto fail_rb; + } + + ssm_rbuff_destroy(rb); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_rb: + while (ssm_rbuff_read(rb) >= 0) + ; + + ssm_rbuff_destroy(rb); + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +/* Ages the seed sample past the estimator's dt floor at write 16. */ +static int test_ssm_rbuff_write_over_limit(void) +{ + struct ssm_rbuff * rb; + struct timespec dfl = TIMESPEC_INIT_MS(SSM_RBUFF_TXQ_DELAY); + struct timespec age = {0, 20 * 1000}; + size_t count; + int ret = 0; + + TEST_START(); + + rb = ssm_rbuff_create(getpid(), 16); + if (rb == NULL) { + printf("Failed to create rbuff.\n"); + goto fail; + } + + ssm_rbuff_set_txq_target(rb, &dfl); + + for (count = 0; count < SSM_RBUFF_SIZE; ++count) { + ret = ssm_rbuff_write(rb, count); + if (ret == -EAGAIN) + break; + + if (ret < 0) { + printf("Write failed at index %zu: %d.\n", count, ret); + goto fail_rb; + } + + if (count == 16) + nanosleep(&age, NULL); + } + + if (ret != -EAGAIN) { + printf("Expected the limiter to reject a write.\n"); + goto fail_rb; + } + + if (count >= SSM_RBUFF_SIZE / 2) { + printf("Expected -EAGAIN well before a full ring, " + "got %zu writes.\n", count); + goto fail_rb; + } + + if (ssm_rbuff_queued(rb) != count) { + printf("Queued %zu does not match write count %zu.\n", + ssm_rbuff_queued(rb), count); + goto fail_rb; + } + + while (ssm_rbuff_read(rb) >= 0) + ; + + ssm_rbuff_destroy(rb); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_rb: + while (ssm_rbuff_read(rb) >= 0) + ; + ssm_rbuff_destroy(rb); fail: TEST_FAIL(); @@ -664,12 +1066,18 @@ int rbuff_test(int argc, ret |= test_ssm_rbuff_write_read(); ret |= test_ssm_rbuff_read_empty(); ret |= test_ssm_rbuff_fill_drain(); - ret |= test_ssm_rbuff_acl(); + ret |= test_ssm_rbuff_flags(); ret |= test_ssm_rbuff_open_close(); ret |= test_ssm_rbuff_threaded(); ret |= test_ssm_rbuff_blocking(); ret |= test_ssm_rbuff_blocking_timeout(); ret |= test_ssm_rbuff_blocking_flowdown(); + ret |= test_ssm_rbuff_limit_off(); + ret |= test_ssm_rbuff_limit_slow(); + ret |= test_ssm_rbuff_limit_fast(); + ret |= test_ssm_rbuff_limit_floor(); + ret |= test_ssm_rbuff_txq_target(); + ret |= test_ssm_rbuff_write_over_limit(); return ret; } |
