23#include <freeradius-devel/util/test/acutest_common_init.h>
24#include <freeradius-devel/util/test/acutest_helpers.h>
27#include <freeradius-devel/bio/bio_priv.h>
28#include <freeradius-devel/bio/null.h>
29#include <freeradius-devel/bio/retry.h>
109 if (!my)
return NULL;
132 if (!cfg->
el)
return NULL;
142 if (!stub)
return NULL;
145 if (!retry)
return NULL;
176 if (!retry)
goto done;
178 TEST_CASE(
"the first packet uses the only entry");
183 TEST_CASE(
"the second write returns IO_WOULD_BLOCK, and the retry bio calls the write_blocked callback");
187 TEST_CASE(
"running out of entries does not set write_blocked");
190 TEST_CASE(
"cancelling the entry calls the release callback and the resume callback");
195 TEST_CASE(
"the retry bio now accepts the second packet");
213 if (!retry)
goto done;
219 TEST_CASE(
"a write from inside the resume callback succeeds");
225 TEST_CASE(
"the retry bio did not call the write_blocked callback a second time");
246 if (!retry)
goto done;
250 TEST_CASE(
"the stub accepts the first transmission in full");
254 TEST_CASE(
"the retransmission timer fires, and the stub accepts one byte");
255 TEST_MSG(
"the initial retransmission time (irt) is 2s, so running the timer list 3s ahead fires exactly one retransmission");
261 TEST_CASE(
"the retry bio has saved the rest of the packet and has blocked writes");
264 TEST_CASE(
"a flush sends the rest of the packet exactly once");
289 if (!retry)
goto done;
293 TEST_CASE(
"a partial write returns OOM when the retry bio cannot allocate the buffer for the rest of the packet");
294 TEST_MSG(
"a talloc memory limit on the retry bio makes the buffer allocation fail");
296 TEST_CHECK(talloc_set_memlimit(retry, talloc_total_size(retry)) == 0);
297DIAG_ON(deprecated-declarations)
301 TEST_CASE(
"the retry bio releases the entry");
304 TEST_CASE(
"the only entry is free again, so the retry bio accepts the next packet");
306 TEST_CHECK(talloc_set_memlimit(retry, 0) == 0);
307DIAG_ON(deprecated-declarations)
337 if (!retry)
goto done;
349 TEST_CASE(
"blocking writes runs the overdue expiry, which releases the entry");
351 TEST_CHECK(common->priv_cb.write_blocked != NULL);
352 if (!common->priv_cb.write_blocked)
goto done;
358 TEST_CASE(
"a released entry while blocked does not resume writes");
379 if (!retry)
goto done;
383 TEST_CASE(
"two packets are written in full");
387 TEST_CASE(
"both retransmissions are due, and the first one blocks writes");
388 TEST_MSG(
"irt is 2s, so running the timer list 3s ahead makes both retransmissions due");
393 TEST_CASE(
"only the first retransmission was written, and writes are blocked");
static int const char char buffer[256]
#define TEST_CHECK_RET(_got, _exp)
fr_bio_write_t _CONST write
write to the underlying bio
static ssize_t fr_bio_write(fr_bio_t *bio, void *packet_ctx, void const *buffer, size_t size)
Write raw data to a bio.
static fr_bio_t * fr_bio_next(fr_bio_t *bio)
fr_bio_io_t read_resume
"unblocked" is too similar to "blocked"
fr_bio_t * fr_bio_retry_alloc(TALLOC_CTX *ctx, size_t max_saved, fr_bio_retry_sent_t sent, fr_bio_retry_response_t response, fr_bio_retry_rewrite_t rewrite, fr_bio_retry_release_t release, fr_bio_retry_config_t const *cfg, fr_bio_t *next)
Allocate a fr_bio_retry_t.
int fr_bio_retry_entry_cancel(fr_bio_t *bio, fr_bio_retry_entry_t *item)
Cancel one item.
fr_bio_retry_info_t const * fr_bio_retry_info(fr_bio_t *bio)
fr_retry_config_t retry_config
base retry config
fr_bio_retry_release_reason_t
fr_event_list_t * el
event list
static int packet_ctx
the tests pass only the address of packet_ctx
static bool retry_response(UNUSED fr_bio_t *bio, UNUSED fr_bio_retry_entry_t **item_p, UNUSED void *packet_ctx, UNUSED const void *buffer, UNUSED size_t size)
static void test_partial_oom_releases(void)
When the retry bio cannot save the rest of a partly written packet, the retry bio releases the entry ...
static int cb_write_blocked(fr_bio_t *bio)
static fr_bio_t * test_retry_alloc_full(TALLOC_CTX *ctx, fr_bio_retry_config_t *cfg, fr_time_delta_t mrd, size_t max_saved)
Allocate a retry bio with one entry, in front of a stub.
static void test_resume_can_write(void)
The application may write from inside the resume callback, so fr_bio_retry_release() must put the fre...
static int cb_write_resume(fr_bio_t *bio)
static void cb_noop_void(fr_bio_t *bio)
static fr_bio_cb_funcs_t test_cb
static uint8_t packet2[4]
static fr_bio_t * test_retry_alloc_mrd(TALLOC_CTX *ctx, fr_bio_retry_config_t *cfg, fr_time_delta_t mrd)
static ssize_t stub_write_one(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, void const *buffer, UNUSED size_t size)
A transport which accepts one byte of each write, so the retry bio saves the rest of the packet.
static void test_partial_retransmit(void)
The retry bio saves the rest of a partly written retransmission once, and a flush sends the rest once...
static uint8_t packet1[4]
static fr_bio_t * resume_write_bio
when set, cb_write_resume() writes packet2 to this bio
static void test_block_expires_overdue(void)
Blocking writes expires an overdue entry, and the expiry sees writes as blocked.
static fr_bio_t * test_retry_alloc(TALLOC_CTX *ctx, fr_bio_retry_config_t *cfg)
static fr_bio_retry_entry_t * saved_item
static ssize_t stub_write_all(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, void const *buffer, size_t size)
A transport which accepts every write in full, and never has anything to read.
static fr_bio_t * stub_alloc(TALLOC_CTX *ctx)
static void retry_release(UNUSED fr_bio_t *bio, UNUSED fr_bio_retry_entry_t *retry_ctx, UNUSED fr_bio_retry_release_reason_t reason)
static void test_block_stops_retries(void)
When one retransmission blocks writes, the other retransmissions due in the same pass wait.
static void test_all_used_resumes(void)
Running out of entries blocks writes, and freeing an entry resumes writes.
static size_t stub_bytes
bytes of packet data which the stub has accepted
static void retry_sent(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED const void *buffer, UNUSED size_t size, fr_bio_retry_entry_t *retry_ctx)
static ssize_t resume_write_rcode
static int cb_noop(fr_bio_t *bio)
void fr_bio_cb_set(fr_bio_t *bio, fr_bio_cb_funcs_t const *cb)
fr_event_list_t * fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_cb_t status, void *status_uctx)
Initialise a new event list.
ssize_t fr_bio_null_read(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void *buffer, UNUSED size_t size)
Always return 0 on read.
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
#define fr_time_add(_a, _b)
Add a time/time delta together.
static fr_time_delta_t fr_time_delta_from_usec(int64_t usec)
#define fr_time_lt(_a, _b)
A time delta, a difference in time measured in nanoseconds.
int fr_timer_list_run(fr_timer_list_t *tl, fr_time_t *when)
Execute any pending events in the event loop.
fr_time_delta_t irt
Initial transmission time.