mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-11 13:43:47 +01:00
234 lines
7.3 KiB
C
234 lines
7.3 KiB
C
/* Copyright (c) 2018-2019, The Tor Project, Inc. */
|
|
/* See LICENSE for licensing information */
|
|
|
|
/**
|
|
* \file test_bwmgt.c
|
|
* \brief tests for bandwidth management / token bucket functions
|
|
*/
|
|
|
|
#define TOKEN_BUCKET_PRIVATE
|
|
|
|
#include "core/or/or.h"
|
|
#include "test/test.h"
|
|
|
|
#include "lib/evloop/token_bucket.h"
|
|
|
|
// an imaginary time, in timestamp units. Chosen so it will roll over.
|
|
static const uint32_t START_TS = UINT32_MAX-10;
|
|
static const int32_t KB = 1024;
|
|
static const uint32_t GB = (UINT64_C(1) << 30);
|
|
|
|
static void
|
|
test_bwmgt_token_buf_init(void *arg)
|
|
{
|
|
(void)arg;
|
|
token_bucket_rw_t b;
|
|
|
|
token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
|
|
// Burst is correct
|
|
tt_uint_op(b.cfg.burst, OP_EQ, 64*KB);
|
|
// Rate is correct, within 1 percent.
|
|
{
|
|
uint32_t ticks_per_sec =
|
|
(uint32_t) monotime_msec_to_approx_coarse_stamp_units(1000);
|
|
uint32_t rate_per_sec = (b.cfg.rate * ticks_per_sec / TICKS_PER_STEP);
|
|
|
|
tt_uint_op(rate_per_sec, OP_GT, 16*KB-160);
|
|
tt_uint_op(rate_per_sec, OP_LT, 16*KB+160);
|
|
}
|
|
// Bucket starts out full:
|
|
tt_uint_op(b.last_refilled_at_timestamp, OP_EQ, START_TS);
|
|
tt_int_op(b.read_bucket.bucket, OP_EQ, 64*KB);
|
|
|
|
done:
|
|
;
|
|
}
|
|
|
|
static void
|
|
test_bwmgt_token_buf_adjust(void *arg)
|
|
{
|
|
(void)arg;
|
|
token_bucket_rw_t b;
|
|
|
|
token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
|
|
|
|
uint32_t rate_orig = b.cfg.rate;
|
|
// Increasing burst
|
|
token_bucket_rw_adjust(&b, 16*KB, 128*KB);
|
|
tt_uint_op(b.cfg.rate, OP_EQ, rate_orig);
|
|
tt_uint_op(b.read_bucket.bucket, OP_EQ, 64*KB);
|
|
tt_uint_op(b.cfg.burst, OP_EQ, 128*KB);
|
|
|
|
// Decreasing burst but staying above bucket
|
|
token_bucket_rw_adjust(&b, 16*KB, 96*KB);
|
|
tt_uint_op(b.cfg.rate, OP_EQ, rate_orig);
|
|
tt_uint_op(b.read_bucket.bucket, OP_EQ, 64*KB);
|
|
tt_uint_op(b.cfg.burst, OP_EQ, 96*KB);
|
|
|
|
// Decreasing burst below bucket,
|
|
token_bucket_rw_adjust(&b, 16*KB, 48*KB);
|
|
tt_uint_op(b.cfg.rate, OP_EQ, rate_orig);
|
|
tt_uint_op(b.read_bucket.bucket, OP_EQ, 48*KB);
|
|
tt_uint_op(b.cfg.burst, OP_EQ, 48*KB);
|
|
|
|
// Changing rate.
|
|
token_bucket_rw_adjust(&b, 32*KB, 48*KB);
|
|
tt_uint_op(b.cfg.rate, OP_GE, rate_orig*2 - 10);
|
|
tt_uint_op(b.cfg.rate, OP_LE, rate_orig*2 + 10);
|
|
tt_uint_op(b.read_bucket.bucket, OP_EQ, 48*KB);
|
|
tt_uint_op(b.cfg.burst, OP_EQ, 48*KB);
|
|
|
|
done:
|
|
;
|
|
}
|
|
|
|
static void
|
|
test_bwmgt_token_buf_dec(void *arg)
|
|
{
|
|
(void)arg;
|
|
token_bucket_rw_t b;
|
|
token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
|
|
|
|
// full-to-not-full.
|
|
tt_int_op(0, OP_EQ, token_bucket_rw_dec_read(&b, KB));
|
|
tt_int_op(b.read_bucket.bucket, OP_EQ, 63*KB);
|
|
|
|
// Full to almost-not-full
|
|
tt_int_op(0, OP_EQ, token_bucket_rw_dec_read(&b, 63*KB - 1));
|
|
tt_int_op(b.read_bucket.bucket, OP_EQ, 1);
|
|
|
|
// almost-not-full to empty.
|
|
tt_int_op(1, OP_EQ, token_bucket_rw_dec_read(&b, 1));
|
|
tt_int_op(b.read_bucket.bucket, OP_EQ, 0);
|
|
|
|
// reset bucket, try full-to-empty
|
|
token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
|
|
tt_int_op(1, OP_EQ, token_bucket_rw_dec_read(&b, 64*KB));
|
|
tt_int_op(b.read_bucket.bucket, OP_EQ, 0);
|
|
|
|
// reset bucket, try underflow.
|
|
token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
|
|
tt_int_op(1, OP_EQ, token_bucket_rw_dec_read(&b, 64*KB + 1));
|
|
tt_int_op(b.read_bucket.bucket, OP_EQ, -1);
|
|
|
|
// A second underflow does not make the bucket empty.
|
|
tt_int_op(0, OP_EQ, token_bucket_rw_dec_read(&b, 1000));
|
|
tt_int_op(b.read_bucket.bucket, OP_EQ, -1001);
|
|
|
|
done:
|
|
;
|
|
}
|
|
|
|
static void
|
|
test_bwmgt_token_buf_refill(void *arg)
|
|
{
|
|
(void)arg;
|
|
token_bucket_rw_t b;
|
|
const uint32_t BW_SEC =
|
|
(uint32_t)monotime_msec_to_approx_coarse_stamp_units(1000);
|
|
token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
|
|
|
|
/* Make the buffer much emptier, then let one second elapse. */
|
|
token_bucket_rw_dec_read(&b, 48*KB);
|
|
tt_int_op(b.read_bucket.bucket, OP_EQ, 16*KB);
|
|
tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC));
|
|
tt_int_op(b.read_bucket.bucket, OP_GT, 32*KB - 300);
|
|
tt_int_op(b.read_bucket.bucket, OP_LT, 32*KB + 300);
|
|
|
|
/* Another half second. */
|
|
tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*3/2));
|
|
tt_int_op(b.read_bucket.bucket, OP_GT, 40*KB - 400);
|
|
tt_int_op(b.read_bucket.bucket, OP_LT, 40*KB + 400);
|
|
tt_uint_op(b.last_refilled_at_timestamp, OP_EQ, START_TS + BW_SEC*3/2);
|
|
|
|
/* No time: nothing happens. */
|
|
{
|
|
const uint32_t bucket_orig = b.read_bucket.bucket;
|
|
tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*3/2));
|
|
tt_int_op(b.read_bucket.bucket, OP_EQ, bucket_orig);
|
|
}
|
|
|
|
/* Another 30 seconds: fill the bucket. */
|
|
tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b,
|
|
START_TS + BW_SEC*3/2 + BW_SEC*30));
|
|
tt_int_op(b.read_bucket.bucket, OP_EQ, b.cfg.burst);
|
|
tt_uint_op(b.last_refilled_at_timestamp, OP_EQ,
|
|
START_TS + BW_SEC*3/2 + BW_SEC*30);
|
|
|
|
/* Another 30 seconds: nothing happens. */
|
|
tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b,
|
|
START_TS + BW_SEC*3/2 + BW_SEC*60));
|
|
tt_int_op(b.read_bucket.bucket, OP_EQ, b.cfg.burst);
|
|
tt_uint_op(b.last_refilled_at_timestamp, OP_EQ,
|
|
START_TS + BW_SEC*3/2 + BW_SEC*60);
|
|
|
|
/* Empty the bucket, let two seconds pass, and make sure that a refill is
|
|
* noticed. */
|
|
tt_int_op(1, OP_EQ, token_bucket_rw_dec_read(&b, b.cfg.burst));
|
|
tt_int_op(0, OP_EQ, b.read_bucket.bucket);
|
|
tt_int_op(1, OP_EQ, token_bucket_rw_refill(&b,
|
|
START_TS + BW_SEC*3/2 + BW_SEC*61));
|
|
tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b,
|
|
START_TS + BW_SEC*3/2 + BW_SEC*62));
|
|
tt_int_op(b.read_bucket.bucket, OP_GT, 32*KB-400);
|
|
tt_int_op(b.read_bucket.bucket, OP_LT, 32*KB+400);
|
|
|
|
/* Underflow the bucket, make sure we detect when it has tokens again. */
|
|
tt_int_op(1, OP_EQ,
|
|
token_bucket_rw_dec_read(&b, b.read_bucket.bucket+16*KB));
|
|
tt_int_op(-16*KB, OP_EQ, b.read_bucket.bucket);
|
|
// half a second passes...
|
|
tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*64));
|
|
tt_int_op(b.read_bucket.bucket, OP_GT, -8*KB-300);
|
|
tt_int_op(b.read_bucket.bucket, OP_LT, -8*KB+300);
|
|
// a second passes
|
|
tt_int_op(1, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*65));
|
|
tt_int_op(b.read_bucket.bucket, OP_GT, 8*KB-400);
|
|
tt_int_op(b.read_bucket.bucket, OP_LT, 8*KB+400);
|
|
|
|
// We step a second backwards, and nothing happens.
|
|
tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*64));
|
|
tt_int_op(b.read_bucket.bucket, OP_GT, 8*KB-400);
|
|
tt_int_op(b.read_bucket.bucket, OP_LT, 8*KB+400);
|
|
|
|
// A ridiculous amount of time passes.
|
|
tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, INT32_MAX));
|
|
tt_int_op(b.read_bucket.bucket, OP_EQ, b.cfg.burst);
|
|
|
|
done:
|
|
;
|
|
}
|
|
|
|
/* Test some helper functions we use within the token bucket interface. */
|
|
static void
|
|
test_bwmgt_token_buf_helpers(void *arg)
|
|
{
|
|
uint32_t ret;
|
|
|
|
(void) arg;
|
|
|
|
/* The returned value will be OS specific but in any case, it should be
|
|
* greater than 1 since we are passing 1GB/sec rate. */
|
|
ret = rate_per_sec_to_rate_per_step(1 * GB);
|
|
tt_u64_op(ret, OP_GT, 1);
|
|
|
|
/* We default to 1 in case rate is 0. */
|
|
ret = rate_per_sec_to_rate_per_step(0);
|
|
tt_u64_op(ret, OP_EQ, 1);
|
|
|
|
done:
|
|
;
|
|
}
|
|
|
|
#define BWMGT(name) \
|
|
{ #name, test_bwmgt_ ## name , 0, NULL, NULL }
|
|
|
|
struct testcase_t bwmgt_tests[] = {
|
|
BWMGT(token_buf_init),
|
|
BWMGT(token_buf_adjust),
|
|
BWMGT(token_buf_dec),
|
|
BWMGT(token_buf_refill),
|
|
BWMGT(token_buf_helpers),
|
|
END_OF_TESTCASES
|
|
};
|