/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */

#include "test-lib.h"
#include "istream.h"
#include "istream-sized.h"

static const struct {
	const char *input;
	uoff_t size;
	int stream_errno;
} tests[] = {
	{ "", 0, 0 },
	{ "", 1, EPIPE },
	{ "a", 1, 0 },
	{ "ab", 1, EINVAL },
	{ "ab", 0, EINVAL },
	{ "ab", UOFF_T_MAX, EPIPE },
};

static void
run_test(const char *sized_input, uoff_t sized_size, int stream_errno)
{
	unsigned int sized_input_len = strlen(sized_input);
	struct istream *input_data, *input;
	const unsigned char *data;
	size_t i, size;
	int ret = 0;

	input_data = test_istream_create_data(sized_input, sized_input_len);
	test_istream_set_allow_eof(input_data, FALSE);
	input = i_stream_create_sized(input_data, sized_size);

	for (i = 1; i < sized_input_len; i++) {
		test_istream_set_size(input_data, i);
		while ((ret = i_stream_read(input)) > 0) ;
		if (ret == -1 && stream_errno != 0)
			break;
		test_assert(ret == 0);
	}
	if (ret == 0) {
		test_istream_set_allow_eof(input_data, TRUE);
		test_istream_set_size(input_data, i);
		while ((ret = i_stream_read(input)) > 0) ;
	}
	test_assert(ret == -1);
	test_assert(input->stream_errno == stream_errno);

	data = i_stream_get_data(input, &size);
	test_assert(size == I_MIN(sized_input_len, sized_size));
	if (size > 0)
		test_assert(memcmp(data, sized_input, size) == 0);
	i_stream_unref(&input);
	i_stream_unref(&input_data);
}

static void test_istream_sized_full(bool exact)
{
	const unsigned char test_data[10] ATTR_NONSTRING = "1234567890";
	struct istream *test_input, *input;
	unsigned int i, j;
	int expected_errno;

	for (i = 1; i < sizeof(test_data)*2; i++) {
		test_input = test_istream_create_data(test_data, sizeof(test_data));
		test_istream_set_allow_eof(test_input, FALSE);
		test_istream_set_size(test_input, 0);

		if (exact)
			input = i_stream_create_sized(test_input, i);
		else
			input = i_stream_create_min_sized(test_input, i);
		for (j = 1; j <= I_MIN(i, sizeof(test_data)); j++) {
			test_assert_idx(i_stream_read(input) == 0, j);
			test_istream_set_size(test_input, j);
			test_assert_idx(i_stream_read(input) == 1, j);
		}
		test_assert_idx(i_stream_read(input) == 0, i);
		if (j <= sizeof(test_data))
			test_istream_set_size(test_input, j);
		else
			test_istream_set_allow_eof(test_input, TRUE);
		test_assert_idx(i_stream_read(input) == -1 && input->eof, i);
		if (i > sizeof(test_data))
			expected_errno = EPIPE;
		else if (i < sizeof(test_data) && exact)
			expected_errno = EINVAL;
		else
			expected_errno = 0;
		test_assert_idx(input->stream_errno == expected_errno, i);
		i_stream_unref(&input);
		i_stream_unref(&test_input);
	}
}

void test_istream_sized(void)
{
	unsigned int i;

	for (i = 0; i < N_ELEMENTS(tests); i++) {
		test_begin(t_strdup_printf("istream sized %u", i+1));
		run_test(tests[i].input, tests[i].size, tests[i].stream_errno);
		test_end();
	}
	test_begin("istream sized");
	test_istream_sized_full(TRUE);
	test_end();

	test_begin("istream sized min");
	test_istream_sized_full(FALSE);
	test_end();
}
