diff options
Diffstat (limited to 'src/lib-test/test-istream.c')
-rw-r--r-- | src/lib-test/test-istream.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/src/lib-test/test-istream.c b/src/lib-test/test-istream.c new file mode 100644 index 0000000..1d20748 --- /dev/null +++ b/src/lib-test/test-istream.c @@ -0,0 +1,166 @@ +/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "memarea.h" +#include "istream-private.h" +#include "test-common.h" + +struct test_istream { + struct istream_private istream; + const void *orig_buffer; + unsigned int skip_diff; + size_t max_pos; + bool allow_eof; +}; + +static void test_buffer_free(unsigned char *buf) +{ + i_free(buf); +} + +static ssize_t test_read(struct istream_private *stream) +{ + struct test_istream *tstream = (struct test_istream *)stream; + unsigned int new_skip_diff; + size_t cur_max; + ssize_t ret; + + i_assert(stream->skip <= stream->pos); + + if (stream->pos - stream->skip >= tstream->istream.max_buffer_size) { + i_assert(stream->skip != stream->pos); + return -2; + } + + if (tstream->max_pos < stream->pos) { + /* we seeked past the end of file. */ + ret = 0; + } else { + /* copy data to a buffer in somewhat random place. this could + help catch bugs. */ + new_skip_diff = i_rand_limit(128); + stream->skip = (stream->skip - tstream->skip_diff) + + new_skip_diff; + stream->pos = (stream->pos - tstream->skip_diff) + + new_skip_diff; + tstream->max_pos = (tstream->max_pos - tstream->skip_diff) + + new_skip_diff; + tstream->skip_diff = new_skip_diff; + + cur_max = tstream->max_pos; + if (stream->max_buffer_size < SIZE_MAX - stream->skip && + cur_max > stream->skip + stream->max_buffer_size) + cur_max = stream->skip + stream->max_buffer_size; + + /* Reallocate the memory area if needed. Use exactly correct + buffer size so valgrind can catch read overflows. If a + correctly sized memarea already exists, use it only if + its refcount is 1. Otherwise with refcount>1 we could be + moving data within an existing memarea, which breaks + snapshots. */ + if (cur_max > 0 && (stream->buffer_size != cur_max || + stream->memarea == NULL || + memarea_get_refcount(stream->memarea) > 1)) { + void *old_w_buffer = stream->w_buffer; + stream->w_buffer = i_malloc(cur_max); + if (stream->buffer_size != 0) { + memcpy(stream->w_buffer, old_w_buffer, + I_MIN(stream->buffer_size, cur_max)); + } + stream->buffer = stream->w_buffer; + stream->buffer_size = cur_max; + + if (stream->memarea != NULL) + memarea_unref(&stream->memarea); + stream->memarea = memarea_init(stream->w_buffer, + stream->buffer_size, + test_buffer_free, + stream->w_buffer); + } + ssize_t size = cur_max - new_skip_diff; + if (size > 0) + memcpy(stream->w_buffer + new_skip_diff, + tstream->orig_buffer, (size_t)size); + + ret = cur_max - stream->pos; + stream->pos = cur_max; + } + + if (ret > 0) + return ret; + else if (!tstream->allow_eof || + stream->pos - tstream->skip_diff < (uoff_t)stream->statbuf.st_size) + return 0; + else { + stream->istream.eof = TRUE; + return -1; + } +} + +static void test_seek(struct istream_private *stream, uoff_t v_offset, + bool mark ATTR_UNUSED) +{ + struct test_istream *tstream = (struct test_istream *)stream; + + stream->istream.v_offset = v_offset; + stream->skip = v_offset + tstream->skip_diff; + stream->pos = stream->skip; +} + +struct istream *test_istream_create_data(const void *data, size_t size) +{ + struct test_istream *tstream; + + tstream = i_new(struct test_istream, 1); + tstream->orig_buffer = data; + + tstream->istream.read = test_read; + tstream->istream.seek = test_seek; + + tstream->istream.istream.blocking = FALSE; + tstream->istream.istream.seekable = TRUE; + i_stream_create(&tstream->istream, NULL, -1, 0); + tstream->istream.statbuf.st_size = tstream->max_pos = size; + tstream->allow_eof = TRUE; + tstream->istream.max_buffer_size = SIZE_MAX; + return &tstream->istream.istream; +} + +struct istream *test_istream_create(const char *data) +{ + return test_istream_create_data(data, strlen(data)); +} + +static struct test_istream *test_istream_find(struct istream *input) +{ + struct istream *in; + + for (in = input; in != NULL; in = in->real_stream->parent) { + if (in->real_stream->read == test_read) + return (struct test_istream *)in->real_stream; + } + i_panic("%s isn't test-istream", i_stream_get_name(input)); +} + +void test_istream_set_allow_eof(struct istream *input, bool allow) +{ + struct test_istream *tstream = test_istream_find(input); + + tstream->allow_eof = allow; +} + +void test_istream_set_max_buffer_size(struct istream *input, size_t size) +{ + struct test_istream *tstream = test_istream_find(input); + + tstream->istream.max_buffer_size = size; +} + +void test_istream_set_size(struct istream *input, uoff_t size) +{ + struct test_istream *tstream = test_istream_find(input); + + if (size > (uoff_t)tstream->istream.statbuf.st_size) + size = (uoff_t)tstream->istream.statbuf.st_size; + tstream->max_pos = size + tstream->skip_diff; +} |