diff options
Diffstat (limited to 'src/lib/test-iostream-pump.c')
-rw-r--r-- | src/lib/test-iostream-pump.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/src/lib/test-iostream-pump.c b/src/lib/test-iostream-pump.c new file mode 100644 index 0000000..f970fba --- /dev/null +++ b/src/lib/test-iostream-pump.c @@ -0,0 +1,325 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "istream.h" +#include "ostream.h" +#include "buffer.h" +#include "str.h" +#include "ioloop.h" +#include "iostream-pump.h" +#include "istream-failure-at.h" +#include "ostream-failure-at.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> + +struct nonblock_ctx { + struct istream *in; + struct ostream *out; + uoff_t pos, max_size; +}; + +static unsigned char data[] = "hello, world"; + +static void completed(enum iostream_pump_status status, int *u0) +{ + /* to somehow discern between error and success .. */ + (*u0) -= (status == IOSTREAM_PUMP_STATUS_INPUT_EOF ? 1 : 2); + io_loop_stop(current_ioloop); +} + +static void failed(int *u0) +{ + *u0 = -1; /* ensure failure */ + io_loop_stop(current_ioloop); +} + +static void pump_nonblocking_timeout(struct nonblock_ctx *ctx) +{ + switch (ctx->pos % 4) { + case 0: + break; + case 1: + /* allow more input */ + if (ctx->in->blocking) + break; + if (ctx->pos/4 == ctx->max_size+1) + test_istream_set_allow_eof(ctx->in, TRUE); + else + test_istream_set_size(ctx->in, ctx->pos/4); + i_stream_set_input_pending(ctx->in, TRUE); + break; + case 2: + break; + case 3: { + /* allow more output. give always one byte less than the + input size so there's something in internal buffer. */ + if (ctx->out->blocking) + break; + size_t size = ctx->pos/4; + if (size > 0) + test_ostream_set_max_output_size(ctx->out, size-1); + break; + } + } + ctx->pos++; +} + +static const char * +run_pump(struct istream *in, struct ostream *out, int *counter, + buffer_t *out_buffer) +{ + struct iostream_pump *pump; + struct ioloop *ioloop = io_loop_create(); + io_loop_set_current(ioloop); + struct nonblock_ctx ctx = { in, out, 0, 0 }; + struct timeout *to2 = NULL; + + if (!in->blocking) { + test_assert(i_stream_get_size(in, TRUE, &ctx.max_size) > 0); + test_istream_set_size(in, 0); + test_istream_set_allow_eof(in, FALSE); + } + if (!out->blocking) { + test_ostream_set_max_output_size(out, 0); + } + if (!in->blocking || !out->blocking) { + to2 = timeout_add_short(0, pump_nonblocking_timeout, &ctx); + } + + pump = iostream_pump_create(in, out); + i_stream_unref(&in); + o_stream_unref(&out); + + iostream_pump_set_completion_callback(pump, completed, counter); + iostream_pump_start(pump); + + alarm(5); + struct timeout *to = timeout_add(3000, failed, counter); + + io_loop_run(current_ioloop); + + timeout_remove(&to); + timeout_remove(&to2); + alarm(0); + + test_assert(*counter == 0); + + if (!ctx.out->blocking && ctx.in->stream_errno != 0 && + ctx.out->stream_errno == 0) { + /* input failed, finish flushing output */ + test_ostream_set_max_output_size(ctx.out, SIZE_MAX); + test_assert(o_stream_flush(ctx.out) > 0); + } else { + test_assert(o_stream_flush(ctx.out) != 0); + } + + const char *ret = t_strdup(str_c(out_buffer)); + + iostream_pump_unref(&pump); + io_loop_destroy(&ioloop); + return ret; +} + +static void +test_iostream_setup(bool in_block, bool out_block, + struct istream **in_r, struct ostream **out_r, + buffer_t **out_buffer_r) +{ + *out_buffer_r = t_buffer_create(128); + + *in_r = test_istream_create_data(data, sizeof(data)); + (*in_r)->blocking = in_block; + + if (out_block) + *out_r = test_ostream_create(*out_buffer_r); + else + *out_r = test_ostream_create_nonblocking(*out_buffer_r, 1); +} + +static void +test_iostream_pump_simple(bool in_block, bool out_block) +{ + int counter; + struct istream *in; + struct ostream *out; + buffer_t *buffer; + + test_begin(t_strdup_printf("iostream_pump " + "(in=%sblocking, out=%sblocking)", + (in_block ? "" : "non-"), + (out_block ? "" : "non-"))); + + test_iostream_setup(in_block, out_block, &in, &out, &buffer); + counter = 1; + + test_assert(strcmp(run_pump(in, out, &counter, buffer), + "hello, world") == 0); + + test_end(); +} + +static void +test_iostream_pump_failure_start_read(bool in_block, bool out_block) +{ + int counter; + struct istream *in, *in_2; + struct ostream *out; + buffer_t *buffer; + + test_begin(t_strdup_printf("iostream_pump failure start-read " + "(in=%sblocking, out=%sblocking)", + (in_block ? "" : "non-"), + (out_block ? "" : "non-"))); + + test_iostream_setup(in_block, out_block, &in_2, &out, &buffer); + in = i_stream_create_failure_at(in_2, 0, EIO, "test pump fail"); + i_stream_unref(&in_2); + counter = 2; + test_assert(strcmp(run_pump(in, out, &counter, buffer), "") == 0); + + test_end(); +} + +static void +test_iostream_pump_failure_mid_read(bool in_block, bool out_block) +{ + int counter; + struct istream *in, *in_2; + struct ostream *out; + buffer_t *buffer; + + test_begin(t_strdup_printf("iostream_pump failure mid-read " + "(in=%sblocking, out=%sblocking)", + (in_block ? "" : "non-"), + (out_block ? "" : "non-"))); + + test_iostream_setup(in_block, out_block, &in_2, &out, &buffer); + in = i_stream_create_failure_at(in_2, 4, EIO, "test pump fail"); + i_stream_unref(&in_2); + counter = 2; + test_assert(strcmp(run_pump(in, out, &counter, buffer), "hell") == 0); + + test_end(); +} + +static void +test_iostream_pump_failure_end_read(bool in_block, bool out_block) +{ + int counter; + struct istream *in, *in_2; + struct ostream *out; + buffer_t *buffer; + + test_begin(t_strdup_printf("iostream_pump failure mid-read " + "(in=%sblocking, out=%sblocking)", + (in_block ? "" : "non-"), + (out_block ? "" : "non-"))); + + test_iostream_setup(in_block, out_block, &in_2, &out, &buffer); + in = i_stream_create_failure_at_eof(in_2, EIO, "test pump fail"); + i_stream_unref(&in_2); + counter = 2; + test_assert(strcmp(run_pump(in, out, &counter, buffer), + "hello, world") == 0); + + test_end(); +} + +static void +test_iostream_pump_failure_start_write(bool in_block, bool out_block) +{ + int counter; + struct istream *in; + struct ostream *out, *out_2; + buffer_t *buffer; + + test_begin(t_strdup_printf("iostream_pump failure start-write " + "(in=%sblocking, out=%sblocking)", + (in_block ? "" : "non-"), + (out_block ? "" : "non-"))); + + test_iostream_setup(in_block, out_block, &in, &out_2, &buffer); + out = o_stream_create_failure_at(out_2, 0, "test pump fail"); + o_stream_unref(&out_2); + counter = 2; + test_assert(strcmp(run_pump(in, out, &counter, buffer), "") == 0); + + test_end(); +} + +static void +test_iostream_pump_failure_mid_write(bool in_block, bool out_block) +{ + int counter; + struct istream *in; + struct ostream *out, *out_2; + buffer_t *buffer; + + test_begin(t_strdup_printf("iostream_pump failure mid-write " + "(in=%sblocking, out=%sblocking)", + (in_block ? "" : "non-"), + (out_block ? "" : "non-"))); + + test_iostream_setup(in_block, out_block, &in, &out_2, &buffer); + out = o_stream_create_failure_at(out_2, 4, "test pump fail"); + o_stream_unref(&out_2); + counter = 2; + + /* "hel" because the last byte is only in internal buffer */ + test_assert(strcmp(run_pump(in, out, &counter, buffer), + (out_block ? (in_block ? "" : "hell") : + "hel")) == 0); + + test_end(); +} + +static void +test_iostream_pump_failure_end_write(bool in_block, bool out_block) +{ + int counter; + struct istream *in; + struct ostream *out, *out_2; + buffer_t *buffer; + + if (!out_block || !in_block) { + /* we'll get flushes constantly */ + return; + } + + test_begin("iostream_pump failure end-write (blocking)"); + + test_iostream_setup(in_block, out_block, &in, &out_2, &buffer); + out = o_stream_create_failure_at_flush(out_2, "test pump fail"); + o_stream_unref(&out_2); + counter = 2; + test_assert(strcmp(run_pump(in, out, &counter, buffer), + "hello, world") == 0); + + test_end(); +} + +static void +test_iostream_pump_real(void) +{ + for(int i = 0; i < 3; i++) { + bool in_block = ((i & BIT(0)) != 0); + bool out_block = ((i & BIT(1)) != 0); + + test_iostream_pump_simple(in_block, out_block); + test_iostream_pump_failure_start_read(in_block, out_block); + test_iostream_pump_failure_mid_read(in_block, out_block); + test_iostream_pump_failure_end_read(in_block, out_block); + test_iostream_pump_failure_start_write(in_block, out_block); + test_iostream_pump_failure_mid_write(in_block, out_block); + test_iostream_pump_failure_end_write(in_block, out_block); + } +} + +void test_iostream_pump(void) +{ + T_BEGIN { + test_iostream_pump_real(); + } T_END; +} |