diff options
Diffstat (limited to 'src/lib/test-istream-seekable.c')
-rw-r--r-- | src/lib/test-istream-seekable.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/src/lib/test-istream-seekable.c b/src/lib/test-istream-seekable.c new file mode 100644 index 0000000..3373f52 --- /dev/null +++ b/src/lib/test-istream-seekable.c @@ -0,0 +1,290 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "sha2.h" +#include "istream-private.h" +#include "istream-sized.h" +#include "istream-hash.h" +#include "istream-seekable.h" + +#include <fcntl.h> +#include <unistd.h> + +static int fd_callback_fd = -1; + +static int fd_callback(const char **path_r, void *context ATTR_UNUSED) +{ + int fd; + + *path_r = "test-lib.tmp"; + fd = open(*path_r, O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fd == -1) + i_error("creat(%s) failed: %m", *path_r); + else + i_unlink(*path_r); + fd_callback_fd = fd; + return fd; +} + +static void test_istream_seekable_one(unsigned int buffer_size) +{ + static const char *input_string = "xyz"; +#define STREAM_COUNT 5 +#define STREAM_BYTES 3 + struct istream *streams[STREAM_COUNT+1]; + struct istream *input; + const unsigned char *data; + size_t size; + unsigned int i, j; + + for (i = 0; i < STREAM_COUNT; i++) { + streams[i] = test_istream_create(input_string); + streams[i]->seekable = FALSE; + test_istream_set_allow_eof(streams[i], TRUE); + test_istream_set_size(streams[i], 0); + } + streams[i] = NULL; + + input = i_stream_create_seekable(streams, buffer_size, fd_callback, NULL); + test_assert(!input->blocking); + for (i = 0; i/STREAM_BYTES < STREAM_COUNT; i++) { + test_istream_set_size(streams[i/STREAM_BYTES], (i%STREAM_BYTES) + 1); + if (i < buffer_size) { + test_assert(i_stream_read(input) == 1); + data = i_stream_get_data(input, &size); + test_assert(size == i+1); + } else { + test_assert(i_stream_read(input) == -2); + i_stream_skip(input, 1); + test_assert(i_stream_read(input) == 1); + data = i_stream_get_data(input, &size); + test_assert(size == buffer_size); + } + for (j = 0; j < size; j++) { + test_assert((char)data[j] == input_string[(input->v_offset + j) % STREAM_BYTES]); + } + } + test_assert(!input->blocking); + test_assert(i_stream_read(input) == -1); + test_assert(input->blocking); + for (i = 0; i < STREAM_COUNT; i++) { + test_assert(streams[i]->eof && streams[i]->stream_errno == 0); + i_stream_unref(&streams[i]); + } + i_stream_unref(&input); +} + +static void test_istream_seekable_random(void) +{ + struct istream **streams, *input; + const unsigned char *data; + unsigned char *w_data; + size_t size; + unsigned int i, j, offset, stream_count, data_len, buffer_size; + + stream_count = i_rand_minmax(2, 10 + 2 - 1); + streams = t_new(struct istream *, stream_count + 1); + for (i = 0, offset = 0; i < stream_count; i++) { + data_len = i_rand_minmax(1, 100); + w_data = t_malloc_no0(data_len); + for (j = 0; j < data_len; j++) + w_data[j] = (offset++) & 0xff; + streams[i] = test_istream_create_data(w_data, data_len); + streams[i]->seekable = FALSE; + test_istream_set_allow_eof(streams[i], TRUE); + } + streams[i] = NULL; + i_assert(offset > 0); + + buffer_size = i_rand_minmax(1, 100); size = 0; + input = i_stream_create_seekable(streams, buffer_size, fd_callback, NULL); + test_assert(!input->blocking); + + /* first read it through */ + while (i_stream_read(input) > 0) { + size = i_stream_get_data_size(input); + i_stream_skip(input, size); + } + test_assert(input->blocking); + + i_stream_seek(input, 0); + for (i = 0; i < 100; i++) { + if (i_rand_limit(3) == 0) { + i_stream_seek(input, i_rand_limit(offset)); + } else { + ssize_t ret = i_stream_read(input); + if (input->v_offset + size == offset) + test_assert(ret < 0); + else if (ret == -2) { + test_assert(size == buffer_size); + } else { + test_assert(ret > 0); + test_assert(input->v_offset + ret <= offset); + i_stream_skip(input, i_rand_limit(ret + 1)); + + data = i_stream_get_data(input, &size); + for (j = 0; j < size; j++) { + test_assert(data[j] == (input->v_offset + j) % 256); + } + } + } + size = i_stream_get_data_size(input); + } + for (i = 0; i < stream_count; i++) { + test_assert(streams[i]->eof && streams[i]->stream_errno == 0); + i_stream_unref(&streams[i]); + } + i_stream_unref(&input); +} + +static void test_istream_seekable_eof(void) +{ + static const char *in_str = "foo"; + unsigned int in_str_len = strlen(in_str); + struct istream *streams[2], *input; + const unsigned char *data; + size_t size; + + test_begin("istream seekable eof"); + + streams[0] = i_stream_create_from_data(in_str, in_str_len); + streams[0]->seekable = FALSE; + streams[1] = NULL; + + input = i_stream_create_seekable(streams, in_str_len, fd_callback, NULL); + + test_assert(i_stream_read(input) == (ssize_t)in_str_len); + data = i_stream_get_data(input, &size); + test_assert(size == in_str_len); + test_assert(memcmp(data, in_str, in_str_len) == 0); + + test_assert(i_stream_read(input) == -1); + data = i_stream_get_data(input, &size); + test_assert(size == in_str_len); + test_assert(memcmp(data, in_str, in_str_len) == 0); + i_stream_seek(input, size); + + i_stream_unref(&input); + + test_assert(streams[0]->v_offset == in_str_len); + test_assert(streams[0]->eof); + i_stream_unref(&streams[0]); + test_end(); +} + +static void test_istream_seekable_early_end(void) +{ + struct istream *input, *streams[2]; + + test_begin("istream seekable early end"); + + streams[0] = test_istream_create("stream"); + test_istream_set_size(streams[0], 3); + test_istream_set_allow_eof(streams[0], FALSE); + streams[0]->seekable = FALSE; + streams[1] = NULL; + + input = i_stream_create_seekable(streams, 1000, fd_callback, NULL); + test_assert(i_stream_read(input) == 3); + test_istream_set_size(streams[0], 5); + test_assert(i_stream_read(input) == 2); + i_stream_skip(input, 5); + i_stream_unref(&input); + + test_assert(streams[0]->v_offset == 5); + i_stream_unref(&streams[0]); + + test_end(); +} + +static void test_istream_seekable_invalid_read(void) +{ + test_begin("istream seekable + other streams causing invalid read"); + struct sha256_ctx hash_ctx; + sha256_init(&hash_ctx); + struct istream *str_input = test_istream_create("123456"); + str_input->seekable = FALSE; + struct istream *seek_inputs[] = { str_input, NULL }; + struct istream *seek_input = i_stream_create_seekable(seek_inputs, 3, fd_callback, NULL); + struct istream *sized_input = i_stream_create_sized(seek_input, 3); + struct istream *input = i_stream_create_hash(sized_input, &hash_method_sha256, &hash_ctx); + test_assert(i_stream_read(input) == 3); + test_assert(i_stream_read(input) == -2); + i_stream_skip(input, 3); + test_assert(i_stream_read(input) == -1); + i_stream_unref(&input); + i_stream_unref(&sized_input); + i_stream_unref(&seek_input); + i_stream_unref(&str_input); + test_end(); +} + +static void test_istream_seekable_get_size(void) +{ + test_begin("istream seekable get size"); + struct istream *str_input = test_istream_create("123456"); + str_input->seekable = FALSE; + struct istream *seek_inputs[] = { str_input, NULL }; + struct istream *input = i_stream_create_seekable(seek_inputs, 32, fd_callback, NULL); + uoff_t size; + test_assert(i_stream_read(input) == 6); + test_assert(i_stream_read(input) == -1); + test_assert(i_stream_get_size(input, TRUE, &size) == 1 && + size == 6); + i_stream_unref(&input); + i_stream_unref(&str_input); + test_end(); +} + +static void test_istream_seekable_failed_writes(void) +{ + struct istream *input, *streams[2]; + + test_begin("istream seekable failed write"); + streams[0] = test_istream_create("stream"); + test_istream_set_size(streams[0], 3); + test_istream_set_allow_eof(streams[0], FALSE); + streams[0]->seekable = FALSE; + streams[1] = NULL; + + input = i_stream_create_seekable(streams, 2, fd_callback, NULL); + i_stream_set_name(input, "test seekable"); + test_assert(i_stream_read(input) == 2); + i_stream_skip(input, 2); + test_assert(i_stream_read(input) == 1); + i_close_fd(&fd_callback_fd); + test_istream_set_size(streams[0], 5); + + test_expect_error_string("istream-seekable: write_full(test-lib.tmp) failed: Bad file descriptor"); + test_assert(i_stream_read(input) == -1); + test_expect_no_more_errors(); + + test_expect_error_string("file_istream.close((seekable temp-istream for: test seekable)) failed: Bad file descriptor"); + i_stream_unref(&input); + test_expect_no_more_errors(); + + i_stream_unref(&streams[0]); + test_end(); +} + +void test_istream_seekable(void) +{ + unsigned int i; + + test_begin("istream seekable"); + for (i = 1; i <= STREAM_BYTES*STREAM_COUNT; i++) + test_istream_seekable_one(i); + test_end(); + + test_begin("istream seekable random"); + for (i = 0; i < 100; i++) T_BEGIN { + test_istream_seekable_random(); + } T_END; + test_end(); + + test_istream_seekable_eof(); + test_istream_seekable_early_end(); + test_istream_seekable_invalid_read(); + test_istream_seekable_get_size(); + test_istream_seekable_failed_writes(); +} |