tor/src/test/test_buffers.c

824 lines
23 KiB
C
Raw Normal View History

/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
2019-01-16 18:32:32 +01:00
* Copyright (c) 2007-2019, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define BUFFERS_PRIVATE
#define PROTO_HTTP_PRIVATE
#include "core/or/or.h"
2018-06-28 22:29:35 +02:00
#include "lib/container/buffers.h"
2018-06-21 18:47:11 +02:00
#include "lib/tls/buffers_tls.h"
#include "lib/tls/tortls.h"
#include "lib/compress/compress.h"
2018-06-21 18:47:11 +02:00
#include "lib/crypt_ops/crypto_rand.h"
#include "core/proto/proto_http.h"
#include "core/proto/proto_socks.h"
2018-06-20 15:35:05 +02:00
#include "test/test.h"
/** Run unit tests for buffers.c */
static void
test_buffers_basic(void *arg)
{
char str[256];
char str2[256];
buf_t *buf = NULL, *buf2 = NULL;
const char *cp;
int j;
size_t r;
(void) arg;
/****
* buf_new
****/
if (!(buf = buf_new()))
Use coccinelle scripts to clean up our unit tests This should get rid of most of the users of the old test_* functions. Some are in macros and will need manual cleanup, though. This patch is for 13119, and was automatically generated with these scripts. The perl scripts are there because coccinelle hates operators as macro arguments. ------------------------------ s/==,/_X_EQ_,/g; s/!=,/_X_NE_,/g; s/<,/_X_LT_,/g; s/>,/_X_GT_,/g; s/>=,/_X_GEQ_,/g; s/<=,/_X_LEQ_,/g; ------------------------------ @@ expression a; identifier func; @@ func (...) { <... -test_fail_msg +TT_DIE ( +( a +) ) ...> } @@ identifier func; @@ func (...) { <... -test_fail() +TT_DIE(("Assertion failed.")) ...> } @@ expression a; identifier func; @@ func (...) { <... -test_assert +tt_assert (a) ...> } @@ expression a, b; identifier func; @@ func (...) { <... -test_eq +tt_int_op (a, +_X_EQ_, b) ...> } @@ expression a, b; identifier func; @@ func (...) { <... -test_neq +tt_int_op (a, +_X_NEQ_, b) ...> } @@ expression a, b; identifier func; @@ func (...) { <... -test_streq +tt_str_op (a, +_X_EQ_, b) ...> } @@ expression a, b; identifier func; @@ func (...) { <... -test_strneq +tt_str_op (a, +_X_NEQ_, b) ...> } @@ expression a, b; identifier func; @@ func (...) { <... -test_eq_ptr +tt_ptr_op (a, +_X_EQ_, b) ...> } @@ expression a, b; identifier func; @@ func() { <... -test_neq_ptr +tt_ptr_op (a, +_X_NEQ_, b) ...> } @@ expression a, b, len; identifier func; @@ func (...) { <... -test_memeq +tt_mem_op (a, +_X_EQ_, b, len) ...> } @@ expression a, b, len; identifier func; @@ func (...) { <... -test_memneq +tt_mem_op (a, +_X_NEQ_, b, len) ...> } ------------------------------ @@ char a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a == b +a, _X_EQ_, b ) ...> } @@ int a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a == b +a, _X_EQ_, b ) ...> } @@ long a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a == b +a, _X_EQ_, b ) ...> } @@ unsigned int a, b; identifier func; @@ func (...) { <... -tt_assert +tt_uint_op ( -a == b +a, _X_EQ_, b ) ...> } @@ unsigned long a, b; identifier func; @@ func (...) { <... -tt_assert +tt_uint_op ( -a == b +a, _X_EQ_, b ) ...> } @@ char a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a != b +a, _X_NEQ_, b ) ...> } @@ int a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a != b +a, _X_NEQ_, b ) ...> } @@ long a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a != b +a, _X_NEQ_, b ) ...> } @@ unsigned int a, b; identifier func; @@ func (...) { <... -tt_assert +tt_uint_op ( -a != b +a, _X_NEQ_, b ) ...> } @@ unsigned long a, b; identifier func; @@ func (...) { <... -tt_assert +tt_uint_op ( -a != b +a, _X_NEQ_, b ) ...> } @@ char a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a >= b +a, _X_GEQ_, b ) ...> } @@ int a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a >= b +a, _X_GEQ_, b ) ...> } @@ long a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a >= b +a, _X_GEQ_, b ) ...> } @@ unsigned int a, b; identifier func; @@ func (...) { <... -tt_assert +tt_uint_op ( -a >= b +a, _X_GEQ_, b ) ...> } @@ unsigned long a, b; identifier func; @@ func (...) { <... -tt_assert +tt_uint_op ( -a >= b +a, _X_GEQ_, b ) ...> } @@ char a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a <= b +a, _X_LEQ_, b ) ...> } @@ int a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a <= b +a, _X_LEQ_, b ) ...> } @@ long a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a <= b +a, _X_LEQ_, b ) ...> } @@ unsigned int a, b; identifier func; @@ func (...) { <... -tt_assert +tt_uint_op ( -a <= b +a, _X_LEQ_, b ) ...> } @@ unsigned long a, b; identifier func; @@ func (...) { <... -tt_assert +tt_uint_op ( -a <= b +a, _X_LEQ_, b ) ...> } @@ char a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a > b +a, _X_GT_, b ) ...> } @@ int a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a > b +a, _X_GT_, b ) ...> } @@ long a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a > b +a, _X_GT_, b ) ...> } @@ unsigned int a, b; identifier func; @@ func (...) { <... -tt_assert +tt_uint_op ( -a > b +a, _X_GT_, b ) ...> } @@ unsigned long a, b; identifier func; @@ func (...) { <... -tt_assert +tt_uint_op ( -a > b +a, _X_GT_, b ) ...> } @@ char a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a < b +a, _X_LT_, b ) ...> } @@ int a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a < b +a, _X_LT_, b ) ...> } @@ long a, b; identifier func; @@ func (...) { <... -tt_assert +tt_int_op ( -a < b +a, _X_LT_, b ) ...> } @@ unsigned int a, b; identifier func; @@ func (...) { <... -tt_assert +tt_uint_op ( -a < b +a, _X_LT_, b ) ...> } @@ unsigned long a, b; identifier func; @@ func (...) { <... -tt_assert +tt_uint_op ( -a < b +a, _X_LT_, b ) ...> } ------------------------------ s/_X_NEQ_/!=/g; s/_X_NE_/!=/g; s/_X_EQ_/==/g; s/_X_GT_/>/g; s/_X_LT_/</g; s/_X_GEQ_/>=/g; s/_X_LEQ_/<=/g; s/test_mem_op\(/tt_mem_op\(/g;
2014-09-16 03:18:21 +02:00
TT_DIE(("Assertion failed."));
//test_eq(buf_capacity(buf), 4096);
tt_int_op(buf_datalen(buf),OP_EQ, 0);
/****
* General pointer frobbing
*/
for (j=0;j<256;++j) {
str[j] = (char)j;
}
buf_add(buf, str, 256);
buf_add(buf, str, 256);
tt_int_op(buf_datalen(buf),OP_EQ, 512);
buf_get_bytes(buf, str2, 200);
tt_mem_op(str,OP_EQ, str2, 200);
tt_int_op(buf_datalen(buf),OP_EQ, 312);
memset(str2, 0, sizeof(str2));
buf_get_bytes(buf, str2, 256);
tt_mem_op(str+200,OP_EQ, str2, 56);
tt_mem_op(str,OP_EQ, str2+56, 200);
tt_int_op(buf_datalen(buf),OP_EQ, 56);
memset(str2, 0, sizeof(str2));
/* Okay, now we should be 512 bytes into the 4096-byte buffer. If we add
* another 3584 bytes, we hit the end. */
for (j=0;j<15;++j) {
buf_add(buf, str, 256);
}
buf_assert_ok(buf);
tt_int_op(buf_datalen(buf),OP_EQ, 3896);
buf_get_bytes(buf, str2, 56);
tt_int_op(buf_datalen(buf),OP_EQ, 3840);
tt_mem_op(str+200,OP_EQ, str2, 56);
for (j=0;j<15;++j) {
memset(str2, 0, sizeof(str2));
buf_get_bytes(buf, str2, 256);
tt_mem_op(str,OP_EQ, str2, 256);
}
tt_int_op(buf_datalen(buf),OP_EQ, 0);
buf_free(buf);
buf = NULL;
/* Okay, now make sure growing can work. */
buf = buf_new_with_capacity(16);
//test_eq(buf_capacity(buf), 16);
buf_add(buf, str+1, 255);
//test_eq(buf_capacity(buf), 256);
buf_get_bytes(buf, str2, 254);
tt_mem_op(str+1,OP_EQ, str2, 254);
//test_eq(buf_capacity(buf), 256);
buf_assert_ok(buf);
buf_add(buf, str, 32);
//test_eq(buf_capacity(buf), 256);
buf_assert_ok(buf);
buf_add(buf, str, 256);
buf_assert_ok(buf);
//test_eq(buf_capacity(buf), 512);
tt_int_op(buf_datalen(buf),OP_EQ, 33+256);
buf_get_bytes(buf, str2, 33);
tt_int_op(*str2,OP_EQ, str[255]);
tt_mem_op(str2+1,OP_EQ, str, 32);
//test_eq(buf_capacity(buf), 512);
tt_int_op(buf_datalen(buf),OP_EQ, 256);
buf_get_bytes(buf, str2, 256);
tt_mem_op(str,OP_EQ, str2, 256);
/* now try shrinking: case 1. */
buf_free(buf);
buf = buf_new_with_capacity(33668);
for (j=0;j<67;++j) {
buf_add(buf, str,255);
}
//test_eq(buf_capacity(buf), 33668);
tt_int_op(buf_datalen(buf),OP_EQ, 17085);
for (j=0; j < 40; ++j) {
buf_get_bytes(buf, str2, 255);
tt_mem_op(str2,OP_EQ, str, 255);
}
/* now try shrinking: case 2. */
buf_free(buf);
buf = buf_new_with_capacity(33668);
for (j=0;j<67;++j) {
buf_add(buf, str, 255);
}
for (j=0; j < 20; ++j) {
buf_get_bytes(buf, str2, 255);
tt_mem_op(str2,OP_EQ, str, 255);
}
for (j=0;j<80;++j) {
buf_add(buf, str, 255);
}
//test_eq(buf_capacity(buf),33668);
for (j=0; j < 120; ++j) {
buf_get_bytes(buf, str2, 255);
tt_mem_op(str2,OP_EQ, str, 255);
}
/* Move from buf to buf. */
buf_free(buf);
buf = buf_new_with_capacity(4096);
buf2 = buf_new_with_capacity(4096);
for (j=0;j<100;++j)
buf_add(buf, str, 255);
tt_int_op(buf_datalen(buf),OP_EQ, 25500);
for (j=0;j<100;++j) {
r = 10;
buf_move_to_buf(buf2, buf, &r);
tt_int_op(r,OP_EQ, 0);
}
tt_int_op(buf_datalen(buf),OP_EQ, 24500);
tt_int_op(buf_datalen(buf2),OP_EQ, 1000);
for (j=0;j<3;++j) {
buf_get_bytes(buf2, str2, 255);
tt_mem_op(str2,OP_EQ, str, 255);
}
r = 8192; /*big move*/
buf_move_to_buf(buf2, buf, &r);
tt_int_op(r,OP_EQ, 0);
r = 30000; /* incomplete move */
buf_move_to_buf(buf2, buf, &r);
tt_int_op(r,OP_EQ, 13692);
for (j=0;j<97;++j) {
buf_get_bytes(buf2, str2, 255);
tt_mem_op(str2,OP_EQ, str, 255);
}
buf_free(buf);
buf_free(buf2);
buf = buf2 = NULL;
buf = buf_new_with_capacity(5);
cp = "Testing. This is a moderately long Testing string.";
for (j = 0; cp[j]; j++)
buf_add(buf, cp+j, 1);
tt_int_op(0,OP_EQ, buf_find_string_offset(buf, "Testing", 7));
tt_int_op(1,OP_EQ, buf_find_string_offset(buf, "esting", 6));
tt_int_op(1,OP_EQ, buf_find_string_offset(buf, "est", 3));
tt_int_op(39,OP_EQ, buf_find_string_offset(buf, "ing str", 7));
tt_int_op(35,OP_EQ, buf_find_string_offset(buf, "Testing str", 11));
tt_int_op(32,OP_EQ, buf_find_string_offset(buf, "ng ", 3));
tt_int_op(43,OP_EQ, buf_find_string_offset(buf, "string.", 7));
tt_int_op(-1,OP_EQ, buf_find_string_offset(buf, "shrdlu", 6));
tt_int_op(-1,OP_EQ, buf_find_string_offset(buf, "Testing thing", 13));
tt_int_op(-1,OP_EQ, buf_find_string_offset(buf, "ngx", 3));
buf_free(buf);
buf = NULL;
/* Try adding a string too long for any freelist. */
{
char *mem = tor_malloc_zero(65536);
buf = buf_new();
buf_add(buf, mem, 65536);
tor_free(mem);
tt_int_op(buf_datalen(buf), OP_EQ, 65536);
buf_free(buf);
buf = NULL;
}
done:
if (buf)
buf_free(buf);
if (buf2)
buf_free(buf2);
}
static void
test_buffer_pullup(void *arg)
{
buf_t *buf;
char *stuff, *tmp;
const char *cp;
size_t sz;
(void)arg;
stuff = tor_malloc(16384);
tmp = tor_malloc(16384);
buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
tt_assert(buf);
tt_int_op(buf_get_default_chunk_size(buf), OP_EQ, 4096);
tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
/* There are a bunch of cases for pullup. One is the trivial case. Let's
mess around with an empty buffer. */
buf_pullup(buf, 16, &cp, &sz);
tt_ptr_op(cp, OP_EQ, NULL);
tt_uint_op(sz, OP_EQ, 0);
/* Let's make sure nothing got allocated */
tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
/* Case 1: everything puts into the first chunk with some moving. */
/* Let's add some data. */
crypto_rand(stuff, 16384);
buf_add(buf, stuff, 3000);
buf_add(buf, stuff+3000, 3000);
buf_pullup(buf, 0, &cp, &sz);
tt_ptr_op(cp, OP_NE, NULL);
tt_int_op(sz, OP_LE, 4096);
/* Make room for 3000 bytes in the first chunk, so that the pullup-move code
* can get tested. */
tt_int_op(buf_get_bytes(buf, tmp, 3000), OP_EQ, 3000);
tt_mem_op(tmp,OP_EQ, stuff, 3000);
buf_pullup(buf, 2048, &cp, &sz);
buf_assert_ok(buf);
tt_ptr_op(cp, OP_NE, NULL);
tt_int_op(sz, OP_GE, 2048);
tt_mem_op(cp,OP_EQ, stuff+3000, 2048);
tt_int_op(3000, OP_EQ, buf_datalen(buf));
tt_int_op(buf_get_bytes(buf, tmp, 3000), OP_EQ, 0);
tt_mem_op(tmp,OP_EQ, stuff+3000, 2048);
buf_free(buf);
/* Now try the large-chunk case. */
buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
buf_add(buf, stuff, 4000);
buf_add(buf, stuff+4000, 4000);
buf_add(buf, stuff+8000, 4000);
buf_add(buf, stuff+12000, 4000);
tt_int_op(buf_datalen(buf), OP_EQ, 16000);
buf_pullup(buf, 0, &cp, &sz);
tt_ptr_op(cp, OP_NE, NULL);
tt_int_op(sz, OP_LE, 4096);
buf_pullup(buf, 12500, &cp, &sz);
buf_assert_ok(buf);
tt_ptr_op(cp, OP_NE, NULL);
tt_int_op(sz, OP_GE, 12500);
tt_mem_op(cp,OP_EQ, stuff, 12500);
tt_int_op(buf_datalen(buf), OP_EQ, 16000);
buf_get_bytes(buf, tmp, 12400);
tt_mem_op(tmp,OP_EQ, stuff, 12400);
tt_int_op(buf_datalen(buf), OP_EQ, 3600);
buf_get_bytes(buf, tmp, 3500);
tt_mem_op(tmp,OP_EQ, stuff+12400, 3500);
buf_get_bytes(buf, tmp, 100);
tt_mem_op(tmp,OP_EQ, stuff+15900, 10);
buf_free(buf);
/* Make sure that the pull-up-whole-buffer case works */
buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
buf_add(buf, stuff, 4000);
buf_add(buf, stuff+4000, 4000);
buf_get_bytes(buf, tmp, 100); /* dump 100 bytes from first chunk */
buf_pullup(buf, 16000, &cp, &sz);
buf_assert_ok(buf);
tt_ptr_op(cp, OP_NE, NULL);
tt_int_op(sz, OP_EQ, 7900);
tt_mem_op(cp,OP_EQ, stuff+100, 7900);
buf_free(buf);
buf = NULL;
tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
done:
buf_free(buf);
tor_free(stuff);
tor_free(tmp);
}
static void
test_buffer_copy(void *arg)
{
buf_t *buf=NULL, *buf2=NULL;
const char *s;
size_t len;
char b[256];
int i;
(void)arg;
buf = buf_new();
tt_assert(buf);
/* Copy an empty buffer. */
tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf));
tt_assert(buf2);
tt_int_op(0, OP_EQ, buf_datalen(buf2));
/* Now try with a short buffer. */
s = "And now comes an act of enormous enormance!";
len = strlen(s);
buf_add(buf, s, len);
tt_int_op(len, OP_EQ, buf_datalen(buf));
/* Add junk to buf2 so we can test replacing.*/
buf_add(buf2, "BLARG", 5);
tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf));
tt_int_op(len, OP_EQ, buf_datalen(buf2));
buf_get_bytes(buf2, b, len);
tt_mem_op(b, OP_EQ, s, len);
/* Now free buf2 and retry so we can test allocating */
buf_free(buf2);
buf2 = NULL;
tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf));
tt_int_op(len, OP_EQ, buf_datalen(buf2));
buf_get_bytes(buf2, b, len);
tt_mem_op(b, OP_EQ, s, len);
/* Clear buf for next test */
buf_get_bytes(buf, b, len);
tt_int_op(buf_datalen(buf),OP_EQ,0);
/* Okay, now let's try a bigger buffer. */
s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit "
"esse quam nihil molestiae consequatur, vel illum qui dolorem eum "
"fugiat quo voluptas nulla pariatur?";
len = strlen(s);
for (i = 0; i < 256; ++i) {
b[0]=i;
buf_add(buf, b, 1);
buf_add(buf, s, len);
}
tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf));
tt_int_op(buf_datalen(buf2), OP_EQ, buf_datalen(buf));
for (i = 0; i < 256; ++i) {
buf_get_bytes(buf2, b, len+1);
tt_int_op((unsigned char)b[0],OP_EQ,i);
tt_mem_op(b+1, OP_EQ, s, len);
}
done:
if (buf)
buf_free(buf);
if (buf2)
buf_free(buf2);
}
static void
test_buffer_allocation_tracking(void *arg)
{
2014-01-06 04:27:37 +01:00
char *junk = tor_malloc(16384);
buf_t *buf1 = NULL, *buf2 = NULL;
2014-01-06 04:27:37 +01:00
int i;
(void)arg;
2014-01-06 04:27:37 +01:00
crypto_rand(junk, 16384);
tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
buf1 = buf_new();
tt_assert(buf1);
buf2 = buf_new();
tt_assert(buf2);
tt_int_op(buf_allocation(buf1), OP_EQ, 0);
tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
2014-01-06 04:27:37 +01:00
buf_add(buf1, junk, 4000);
buf_add(buf1, junk, 4000);
buf_add(buf1, junk, 4000);
buf_add(buf1, junk, 4000);
tt_int_op(buf_allocation(buf1), OP_EQ, 16384);
buf_get_bytes(buf1, junk, 100);
tt_int_op(buf_allocation(buf1), OP_EQ, 16384); /* still 4 4k chunks */
2014-01-06 04:27:37 +01:00
tt_int_op(buf_get_total_allocation(), OP_EQ, 16384);
2014-01-06 04:27:37 +01:00
buf_get_bytes(buf1, junk, 4096); /* drop a 1k chunk... */
tt_int_op(buf_allocation(buf1), OP_EQ, 3*4096); /* now 3 4k chunks */
2014-01-06 04:27:37 +01:00
tt_int_op(buf_get_total_allocation(), OP_EQ, 12288); /* that chunk was really
freed. */
2014-01-06 04:27:37 +01:00
buf_add(buf2, junk, 4000);
tt_int_op(buf_allocation(buf2), OP_EQ, 4096); /* another 4k chunk. */
/*
* We bounce back up to 16384 by allocating a new chunk.
*/
tt_int_op(buf_get_total_allocation(), OP_EQ, 16384);
buf_add(buf2, junk, 4000);
tt_int_op(buf_allocation(buf2), OP_EQ, 8192); /* another 4k chunk. */
2014-11-12 19:42:01 +01:00
tt_int_op(buf_get_total_allocation(),
OP_EQ, 5*4096); /* that chunk was new. */
2014-01-06 04:27:37 +01:00
/* Make a really huge buffer */
for (i = 0; i < 1000; ++i) {
buf_add(buf2, junk, 4000);
2014-01-06 04:27:37 +01:00
}
tt_int_op(buf_allocation(buf2), OP_GE, 4008000);
tt_int_op(buf_get_total_allocation(), OP_GE, 4008000);
2014-01-06 04:27:37 +01:00
buf_free(buf2);
buf2 = NULL;
tt_int_op(buf_get_total_allocation(), OP_LT, 4008000);
tt_int_op(buf_get_total_allocation(), OP_EQ, buf_allocation(buf1));
2014-01-06 04:27:37 +01:00
buf_free(buf1);
buf1 = NULL;
tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
done:
buf_free(buf1);
buf_free(buf2);
tor_free(junk);
2013-07-17 23:51:21 +02:00
}
2014-01-09 18:47:24 +01:00
static void
test_buffer_time_tracking(void *arg)
{
buf_t *buf=NULL, *buf2=NULL;
const time_t START = 1389288246;
const uint64_t START_NSEC = ((uint64_t)START) * 1000000000;
2014-01-09 18:47:24 +01:00
int i;
char tmp[4096];
(void)arg;
crypto_rand(tmp, sizeof(tmp));
monotime_enable_test_mocking();
2014-01-09 18:47:24 +01:00
buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
tt_assert(buf);
monotime_coarse_set_mock_time_nsec(START_NSEC);
const uint32_t START_TS = monotime_coarse_get_stamp();
2014-01-09 18:47:24 +01:00
/* Empty buffer means the timestamp is 0. */
tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS));
tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+1000));
2014-01-09 18:47:24 +01:00
buf_add(buf, "ABCDEFG", 7);
tt_int_op(1000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+1000));
2014-01-09 18:47:24 +01:00
buf2 = buf_copy(buf);
tt_assert(buf2);
2014-11-12 19:42:01 +01:00
tt_int_op(1234, OP_EQ,
buf_get_oldest_chunk_timestamp(buf2, START_TS+1234));
2014-01-09 18:47:24 +01:00
/* Now add more bytes; enough to overflow the first chunk. */
monotime_coarse_set_mock_time_nsec(START_NSEC + 123 * (uint64_t)1000000);
const uint32_t TS2 = monotime_coarse_get_stamp();
2014-01-09 18:47:24 +01:00
for (i = 0; i < 600; ++i)
buf_add(buf, "ABCDEFG", 7);
tt_int_op(4207, OP_EQ, buf_datalen(buf));
2014-01-09 18:47:24 +01:00
/* The oldest bytes are still in the front. */
tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+2000));
2014-01-09 18:47:24 +01:00
/* Once those bytes are dropped, the chunk is still on the first
* timestamp. */
buf_get_bytes(buf, tmp, 100);
tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_TS+2000));
2014-01-09 18:47:24 +01:00
/* But once we discard the whole first chunk, we get the data in the second
* chunk. */
buf_get_bytes(buf, tmp, 4000);
tt_int_op(107, OP_EQ, buf_datalen(buf));
tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS2+2000));
2014-01-09 18:47:24 +01:00
/* This time we'll be grabbing a chunk from the freelist, and making sure
its time gets updated */
monotime_coarse_set_mock_time_nsec(START_NSEC + 5617 * (uint64_t)1000000);
const uint32_t TS3 = monotime_coarse_get_stamp();
2014-01-09 18:47:24 +01:00
for (i = 0; i < 600; ++i)
buf_add(buf, "ABCDEFG", 7);
tt_int_op(4307, OP_EQ, buf_datalen(buf));
2014-01-09 18:47:24 +01:00
tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS2+2000));
buf_get_bytes(buf, tmp, 4000);
buf_get_bytes(buf, tmp, 306);
tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS3));
tt_int_op(383, OP_EQ, buf_get_oldest_chunk_timestamp(buf, TS3+383));
2014-01-09 18:47:24 +01:00
done:
buf_free(buf);
buf_free(buf2);
monotime_disable_test_mocking();
2014-01-09 18:47:24 +01:00
}
2014-05-07 01:29:56 +02:00
static void
test_buffers_compress_fin_at_chunk_end_impl(compress_method_t method,
compression_level_t level)
{
char *msg = NULL;
char *contents = NULL;
char *expanded = NULL;
buf_t *buf = NULL;
tor_compress_state_t *compress_state = NULL;
size_t out_len, in_len;
size_t sz, headerjunk;
buf = buf_new_with_capacity(128); /* will round up */
sz = buf_get_default_chunk_size(buf);
msg = tor_malloc_zero(sz);
buf_add(buf, msg, 1);
tt_assert(buf->head);
/* Fill up the chunk so the compression stuff won't fit in one chunk. */
tt_uint_op(buf->head->memlen, OP_LT, sz);
headerjunk = buf->head->memlen - 7;
buf_add(buf, msg, headerjunk-1);
tt_uint_op(buf->head->datalen, OP_EQ, headerjunk);
tt_uint_op(buf_datalen(buf), OP_EQ, headerjunk);
/* Write an empty string, with finalization on. */
compress_state = tor_compress_new(1, method, level);
tt_int_op(buf_add_compress(buf, compress_state, "", 0, 1), OP_EQ, 0);
in_len = buf_datalen(buf);
contents = tor_malloc(in_len);
tt_int_op(buf_get_bytes(buf, contents, in_len), OP_EQ, 0);
if (method == NO_METHOD) {
tt_uint_op(in_len, OP_EQ, headerjunk);
} else {
tt_uint_op(in_len, OP_GT, headerjunk);
}
tt_int_op(0, OP_EQ, tor_uncompress(&expanded, &out_len,
contents + headerjunk,
in_len - headerjunk,
method, 1,
LOG_WARN));
tt_int_op(out_len, OP_EQ, 0);
tt_assert(expanded);
done:
buf_free(buf);
tor_compress_free(compress_state);
tor_free(contents);
tor_free(expanded);
tor_free(msg);
}
static void
test_buffers_compress_impl(compress_method_t method,
compression_level_t level,
int finalize_with_nil)
2014-05-07 01:29:56 +02:00
{
char *msg = NULL;
char *contents = NULL;
char *expanded = NULL;
buf_t *buf = NULL;
tor_compress_state_t *compress_state = NULL;
2014-05-07 01:29:56 +02:00
size_t out_len, in_len;
int done;
2014-05-07 01:29:56 +02:00
buf = buf_new_with_capacity(128); /* will round up */
compress_state = tor_compress_new(1, method, level);
2014-05-07 01:29:56 +02:00
msg = tor_malloc(512);
crypto_rand(msg, 512);
tt_int_op(buf_add_compress(buf, compress_state,
msg, 128, 0), OP_EQ, 0);
tt_int_op(buf_add_compress(buf, compress_state,
msg+128, 128, 0), OP_EQ, 0);
tt_int_op(buf_add_compress(buf, compress_state,
msg+256, 256, 0), OP_EQ, 0);
done = !finalize_with_nil;
tt_int_op(buf_add_compress(buf, compress_state,
"all done", 9, done), OP_EQ, 0);
if (finalize_with_nil) {
tt_int_op(buf_add_compress(buf, compress_state, "", 0, 1), OP_EQ, 0);
}
2014-05-07 01:29:56 +02:00
in_len = buf_datalen(buf);
contents = tor_malloc(in_len);
tt_int_op(buf_get_bytes(buf, contents, in_len), OP_EQ, 0);
2014-05-07 01:29:56 +02:00
tt_int_op(0, OP_EQ, tor_uncompress(&expanded, &out_len,
contents, in_len,
method, 1,
LOG_WARN));
2014-05-07 01:29:56 +02:00
tt_int_op(out_len, OP_GE, 128);
tt_mem_op(msg, OP_EQ, expanded, 128);
tt_int_op(out_len, OP_GE, 512);
tt_mem_op(msg, OP_EQ, expanded, 512);
tt_int_op(out_len, OP_EQ, 512+9);
tt_mem_op("all done", OP_EQ, expanded+512, 9);
2014-05-07 01:29:56 +02:00
done:
buf_free(buf);
tor_compress_free(compress_state);
2014-05-07 01:29:56 +02:00
tor_free(contents);
tor_free(expanded);
tor_free(msg);
}
static void
test_buffers_compress(void *arg)
{
const char *methodname = arg;
tt_assert(methodname);
compress_method_t method = compression_method_get_by_name(methodname);
tt_int_op(method, OP_NE, UNKNOWN_METHOD);
if (! tor_compress_supports_method(method)) {
tt_skip();
}
compression_level_t levels[] = {
BEST_COMPRESSION,
HIGH_COMPRESSION,
MEDIUM_COMPRESSION,
LOW_COMPRESSION
};
for (unsigned l = 0; l < ARRAY_LENGTH(levels); ++l) {
compression_level_t level = levels[l];
test_buffers_compress_impl(method, level, 0);
test_buffers_compress_impl(method, level, 1);
test_buffers_compress_fin_at_chunk_end_impl(method, level);
}
done:
;
}
static const uint8_t *tls_read_ptr;
static int n_remaining;
static int next_reply_val[16];
static int
mock_tls_read(tor_tls_t *tls, char *cp, size_t len)
{
(void)tls;
int rv = next_reply_val[0];
if (rv > 0) {
int max = rv > (int)len ? (int)len : rv;
if (max > n_remaining)
max = n_remaining;
memcpy(cp, tls_read_ptr, max);
rv = max;
n_remaining -= max;
tls_read_ptr += max;
}
memmove(next_reply_val, next_reply_val + 1, 15*sizeof(int));
return rv;
}
static void
test_buffers_tls_read_mocked(void *arg)
{
uint8_t *mem;
buf_t *buf;
(void)arg;
mem = tor_malloc(64*1024);
crypto_rand((char*)mem, 64*1024);
tls_read_ptr = mem;
n_remaining = 64*1024;
MOCK(tor_tls_read, mock_tls_read);
buf = buf_new();
next_reply_val[0] = 1024;
tt_int_op(128, OP_EQ, buf_read_from_tls(buf, NULL, 128));
next_reply_val[0] = 5000;
next_reply_val[1] = 5000;
tt_int_op(6000, OP_EQ, buf_read_from_tls(buf, NULL, 6000));
done:
UNMOCK(tor_tls_read);
tor_free(mem);
buf_free(buf);
}
static void
test_buffers_chunk_size(void *arg)
{
(void)arg;
const int min = 256;
const int max = 65536;
tt_uint_op(buf_preferred_chunk_size(3), OP_EQ, min);
tt_uint_op(buf_preferred_chunk_size(25), OP_EQ, min);
tt_uint_op(buf_preferred_chunk_size(0), OP_EQ, min);
tt_uint_op(buf_preferred_chunk_size(256), OP_EQ, 512);
tt_uint_op(buf_preferred_chunk_size(65400), OP_EQ, max);
/* Here, we're implicitly saying that the chunk header overhead is
* between 1 and 100 bytes. 24..48 would probably be more accurate. */
tt_uint_op(buf_preferred_chunk_size(65536), OP_GT, 65536);
tt_uint_op(buf_preferred_chunk_size(65536), OP_LT, 65536+100);
tt_uint_op(buf_preferred_chunk_size(165536), OP_GT, 165536);
tt_uint_op(buf_preferred_chunk_size(165536), OP_LT, 165536+100);
done:
;
}
static void
test_buffers_find_contentlen(void *arg)
{
static const struct {
const char *headers;
int r;
int contentlen;
} results[] = {
{ "Blah blah\r\nContent-Length: 1\r\n\r\n", 1, 1 },
{ "Blah blah\r\n\r\n", 0, 0 }, /* no content-len */
{ "Blah blah Content-Length: 1\r\n", 0, 0 }, /* no content-len. */
{ "Blah blah\r\nContent-Length: 100000\r\n", 1, 100000},
{ "Blah blah\r\nContent-Length: 1000000000000000000000000\r\n", -1, 0},
{ "Blah blah\r\nContent-Length: 0\r\n", 1, 0},
{ "Blah blah\r\nContent-Length: -1\r\n", -1, 0},
{ "Blah blah\r\nContent-Length: 1x\r\n", -1, 0},
{ "Blah blah\r\nContent-Length: 1 x\r\n", -1, 0},
{ "Blah blah\r\nContent-Length: 1 \r\n", 1, 1},
{ "Blah blah\r\nContent-Length: \r\n", -1, 0},
{ "Blah blah\r\nContent-Length: ", -1, 0},
{ "Blah blah\r\nContent-Length: 5050", -1, 0},
{ NULL, 0, 0 }
};
int i;
(void)arg;
for (i = 0; results[i].headers; ++i) {
int r;
size_t sz;
size_t headerlen = strlen(results[i].headers);
char * tmp = tor_memdup(results[i].headers, headerlen);/* ensure no eos */
sz = 999; /* to ensure it gets set */
r = buf_http_find_content_length(tmp, headerlen, &sz);
tor_free(tmp);
log_debug(LD_DIR, "%d: %s", i, escaped(results[i].headers));
tt_int_op(r, OP_EQ, results[i].r);
tt_int_op(sz, OP_EQ, results[i].contentlen);
}
done:
;
}
2017-06-21 17:20:33 +02:00
static void
test_buffer_peek_startswith(void *arg)
{
(void)arg;
buf_t *buf;
buf = buf_new();
tt_ptr_op(buf, OP_NE, NULL);
tt_assert(buf_peek_startswith(buf, ""));
tt_assert(! buf_peek_startswith(buf, "X"));
2017-06-21 17:20:33 +02:00
buf_add(buf, "Tor", 3);
2017-06-21 17:20:33 +02:00
tt_assert(buf_peek_startswith(buf, ""));
tt_assert(buf_peek_startswith(buf, "T"));
tt_assert(buf_peek_startswith(buf, "To"));
tt_assert(buf_peek_startswith(buf, "Tor"));
tt_assert(! buf_peek_startswith(buf, "Top"));
tt_assert(! buf_peek_startswith(buf, "For"));
tt_assert(! buf_peek_startswith(buf, "Tork"));
tt_assert(! buf_peek_startswith(buf, "Torpor"));
2017-06-21 17:20:33 +02:00
done:
buf_free(buf);
}
struct testcase_t buffer_tests[] = {
{ "basic", test_buffers_basic, TT_FORK, NULL, NULL },
{ "copy", test_buffer_copy, TT_FORK, NULL, NULL },
{ "pullup", test_buffer_pullup, TT_FORK, NULL, NULL },
2017-06-21 17:20:33 +02:00
{ "startswith", test_buffer_peek_startswith, 0, NULL, NULL },
{ "allocation_tracking", test_buffer_allocation_tracking, TT_FORK,
NULL, NULL },
2014-01-09 18:47:24 +01:00
{ "time_tracking", test_buffer_time_tracking, TT_FORK, NULL, NULL },
{ "tls_read_mocked", test_buffers_tls_read_mocked, 0,
NULL, NULL },
{ "chunk_size", test_buffers_chunk_size, 0, NULL, NULL },
{ "find_contentlen", test_buffers_find_contentlen, 0, NULL, NULL },
{ "compress/zlib", test_buffers_compress, TT_FORK,
&passthrough_setup, (char*)"deflate" },
{ "compress/gzip", test_buffers_compress, TT_FORK,
&passthrough_setup, (char*)"gzip" },
{ "compress/zstd", test_buffers_compress, TT_FORK,
&passthrough_setup, (char*)"x-zstd" },
{ "compress/lzma", test_buffers_compress, TT_FORK,
&passthrough_setup, (char*)"x-tor-lzma" },
{ "compress/none", test_buffers_compress, TT_FORK,
&passthrough_setup, (char*)"identity" },
END_OF_TESTCASES
};