Merge remote-tracking branch 'teor/ticket28318-035' into maint-0.3.5
[tor.git] / src / test / test_bwmgt.c
blob7a1782c2c9158ff4764ec1a31d3efbbb31d981ec
1 /* Copyright (c) 2018, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /**
5 * \file test_bwmgt.c
6 * \brief tests for bandwidth management / token bucket functions
7 */
9 #define TOKEN_BUCKET_PRIVATE
11 #include "core/or/or.h"
12 #include "test/test.h"
14 #include "lib/evloop/token_bucket.h"
16 // an imaginary time, in timestamp units. Chosen so it will roll over.
17 static const uint32_t START_TS = UINT32_MAX-10;
18 static const int32_t KB = 1024;
19 static const uint32_t GB = (UINT64_C(1) << 30);
21 static void
22 test_bwmgt_token_buf_init(void *arg)
24 (void)arg;
25 token_bucket_rw_t b;
27 token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
28 // Burst is correct
29 tt_uint_op(b.cfg.burst, OP_EQ, 64*KB);
30 // Rate is correct, within 1 percent.
32 uint32_t ticks_per_sec =
33 (uint32_t) monotime_msec_to_approx_coarse_stamp_units(1000);
34 uint32_t rate_per_sec = (b.cfg.rate * ticks_per_sec / TICKS_PER_STEP);
36 tt_uint_op(rate_per_sec, OP_GT, 16*KB-160);
37 tt_uint_op(rate_per_sec, OP_LT, 16*KB+160);
39 // Bucket starts out full:
40 tt_uint_op(b.last_refilled_at_timestamp, OP_EQ, START_TS);
41 tt_int_op(b.read_bucket.bucket, OP_EQ, 64*KB);
43 done:
47 static void
48 test_bwmgt_token_buf_adjust(void *arg)
50 (void)arg;
51 token_bucket_rw_t b;
53 token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
55 uint32_t rate_orig = b.cfg.rate;
56 // Increasing burst
57 token_bucket_rw_adjust(&b, 16*KB, 128*KB);
58 tt_uint_op(b.cfg.rate, OP_EQ, rate_orig);
59 tt_uint_op(b.read_bucket.bucket, OP_EQ, 64*KB);
60 tt_uint_op(b.cfg.burst, OP_EQ, 128*KB);
62 // Decreasing burst but staying above bucket
63 token_bucket_rw_adjust(&b, 16*KB, 96*KB);
64 tt_uint_op(b.cfg.rate, OP_EQ, rate_orig);
65 tt_uint_op(b.read_bucket.bucket, OP_EQ, 64*KB);
66 tt_uint_op(b.cfg.burst, OP_EQ, 96*KB);
68 // Decreasing burst below bucket,
69 token_bucket_rw_adjust(&b, 16*KB, 48*KB);
70 tt_uint_op(b.cfg.rate, OP_EQ, rate_orig);
71 tt_uint_op(b.read_bucket.bucket, OP_EQ, 48*KB);
72 tt_uint_op(b.cfg.burst, OP_EQ, 48*KB);
74 // Changing rate.
75 token_bucket_rw_adjust(&b, 32*KB, 48*KB);
76 tt_uint_op(b.cfg.rate, OP_GE, rate_orig*2 - 10);
77 tt_uint_op(b.cfg.rate, OP_LE, rate_orig*2 + 10);
78 tt_uint_op(b.read_bucket.bucket, OP_EQ, 48*KB);
79 tt_uint_op(b.cfg.burst, OP_EQ, 48*KB);
81 done:
85 static void
86 test_bwmgt_token_buf_dec(void *arg)
88 (void)arg;
89 token_bucket_rw_t b;
90 token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
92 // full-to-not-full.
93 tt_int_op(0, OP_EQ, token_bucket_rw_dec_read(&b, KB));
94 tt_int_op(b.read_bucket.bucket, OP_EQ, 63*KB);
96 // Full to almost-not-full
97 tt_int_op(0, OP_EQ, token_bucket_rw_dec_read(&b, 63*KB - 1));
98 tt_int_op(b.read_bucket.bucket, OP_EQ, 1);
100 // almost-not-full to empty.
101 tt_int_op(1, OP_EQ, token_bucket_rw_dec_read(&b, 1));
102 tt_int_op(b.read_bucket.bucket, OP_EQ, 0);
104 // reset bucket, try full-to-empty
105 token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
106 tt_int_op(1, OP_EQ, token_bucket_rw_dec_read(&b, 64*KB));
107 tt_int_op(b.read_bucket.bucket, OP_EQ, 0);
109 // reset bucket, try underflow.
110 token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
111 tt_int_op(1, OP_EQ, token_bucket_rw_dec_read(&b, 64*KB + 1));
112 tt_int_op(b.read_bucket.bucket, OP_EQ, -1);
114 // A second underflow does not make the bucket empty.
115 tt_int_op(0, OP_EQ, token_bucket_rw_dec_read(&b, 1000));
116 tt_int_op(b.read_bucket.bucket, OP_EQ, -1001);
118 done:
122 static void
123 test_bwmgt_token_buf_refill(void *arg)
125 (void)arg;
126 token_bucket_rw_t b;
127 const uint32_t BW_SEC =
128 (uint32_t)monotime_msec_to_approx_coarse_stamp_units(1000);
129 token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
131 /* Make the buffer much emptier, then let one second elapse. */
132 token_bucket_rw_dec_read(&b, 48*KB);
133 tt_int_op(b.read_bucket.bucket, OP_EQ, 16*KB);
134 tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC));
135 tt_int_op(b.read_bucket.bucket, OP_GT, 32*KB - 300);
136 tt_int_op(b.read_bucket.bucket, OP_LT, 32*KB + 300);
138 /* Another half second. */
139 tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*3/2));
140 tt_int_op(b.read_bucket.bucket, OP_GT, 40*KB - 400);
141 tt_int_op(b.read_bucket.bucket, OP_LT, 40*KB + 400);
142 tt_uint_op(b.last_refilled_at_timestamp, OP_EQ, START_TS + BW_SEC*3/2);
144 /* No time: nothing happens. */
146 const uint32_t bucket_orig = b.read_bucket.bucket;
147 tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*3/2));
148 tt_int_op(b.read_bucket.bucket, OP_EQ, bucket_orig);
151 /* Another 30 seconds: fill the bucket. */
152 tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b,
153 START_TS + BW_SEC*3/2 + BW_SEC*30));
154 tt_int_op(b.read_bucket.bucket, OP_EQ, b.cfg.burst);
155 tt_uint_op(b.last_refilled_at_timestamp, OP_EQ,
156 START_TS + BW_SEC*3/2 + BW_SEC*30);
158 /* Another 30 seconds: nothing happens. */
159 tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b,
160 START_TS + BW_SEC*3/2 + BW_SEC*60));
161 tt_int_op(b.read_bucket.bucket, OP_EQ, b.cfg.burst);
162 tt_uint_op(b.last_refilled_at_timestamp, OP_EQ,
163 START_TS + BW_SEC*3/2 + BW_SEC*60);
165 /* Empty the bucket, let two seconds pass, and make sure that a refill is
166 * noticed. */
167 tt_int_op(1, OP_EQ, token_bucket_rw_dec_read(&b, b.cfg.burst));
168 tt_int_op(0, OP_EQ, b.read_bucket.bucket);
169 tt_int_op(1, OP_EQ, token_bucket_rw_refill(&b,
170 START_TS + BW_SEC*3/2 + BW_SEC*61));
171 tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b,
172 START_TS + BW_SEC*3/2 + BW_SEC*62));
173 tt_int_op(b.read_bucket.bucket, OP_GT, 32*KB-400);
174 tt_int_op(b.read_bucket.bucket, OP_LT, 32*KB+400);
176 /* Underflow the bucket, make sure we detect when it has tokens again. */
177 tt_int_op(1, OP_EQ,
178 token_bucket_rw_dec_read(&b, b.read_bucket.bucket+16*KB));
179 tt_int_op(-16*KB, OP_EQ, b.read_bucket.bucket);
180 // half a second passes...
181 tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*64));
182 tt_int_op(b.read_bucket.bucket, OP_GT, -8*KB-300);
183 tt_int_op(b.read_bucket.bucket, OP_LT, -8*KB+300);
184 // a second passes
185 tt_int_op(1, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*65));
186 tt_int_op(b.read_bucket.bucket, OP_GT, 8*KB-400);
187 tt_int_op(b.read_bucket.bucket, OP_LT, 8*KB+400);
189 // We step a second backwards, and nothing happens.
190 tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*64));
191 tt_int_op(b.read_bucket.bucket, OP_GT, 8*KB-400);
192 tt_int_op(b.read_bucket.bucket, OP_LT, 8*KB+400);
194 // A ridiculous amount of time passes.
195 tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, INT32_MAX));
196 tt_int_op(b.read_bucket.bucket, OP_EQ, b.cfg.burst);
198 done:
202 /* Test some helper functions we use within the token bucket interface. */
203 static void
204 test_bwmgt_token_buf_helpers(void *arg)
206 uint32_t ret;
208 (void) arg;
210 /* The returned value will be OS specific but in any case, it should be
211 * greater than 1 since we are passing 1GB/sec rate. */
212 ret = rate_per_sec_to_rate_per_step(1 * GB);
213 tt_u64_op(ret, OP_GT, 1);
215 /* We default to 1 in case rate is 0. */
216 ret = rate_per_sec_to_rate_per_step(0);
217 tt_u64_op(ret, OP_EQ, 1);
219 done:
223 #define BWMGT(name) \
224 { #name, test_bwmgt_ ## name , 0, NULL, NULL }
226 struct testcase_t bwmgt_tests[] = {
227 BWMGT(token_buf_init),
228 BWMGT(token_buf_adjust),
229 BWMGT(token_buf_dec),
230 BWMGT(token_buf_refill),
231 BWMGT(token_buf_helpers),
232 END_OF_TESTCASES