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

#include "test-lib.h"
#include "str.h"
#include "istream-private.h"
#include "istream-crlf.h"

static void test_istream_crlf_input(const char *input)
{
	string_t *output;
	const unsigned char *data;
	size_t size = 0;
	ssize_t ret1, ret2;
	unsigned int i, j, pos, input_len = strlen(input);
	struct istream *istream, *crlf_istream;

	output = t_str_new(256);

	for (j = 0; j < 4; j++) {
		istream = i_stream_create_from_data(input, input_len);
		str_truncate(output, 0);
		if (j%2 == 0) {
			/* drop CRs */
			crlf_istream = i_stream_create_lf(istream);
			for (i = 0; i < input_len; i++) {
				if (input[i] == '\r' &&
				    (i == input_len-1 || input[i+1] == '\n'))
					;
				else
					str_append_c(output, input[i]);
			}
		} else {
			/* add missing CRs */
			crlf_istream = i_stream_create_crlf(istream);
			for (i = 0; i < input_len; i++) {
				if (input[i] == '\n' &&
				    (i == 0 || input[i-1] != '\r'))
					str_append_c(output, '\r');
				str_append_c(output, input[i]);
			}
		}

		pos = 0;
		for (i = 1; i <= input_len; i++) {
			if (j >= 2) {
				i_stream_unref(&istream);
				i_stream_unref(&crlf_istream);
				istream = i_stream_create_from_data(input,
								    input_len);
				crlf_istream = j%2 == 0 ?
					i_stream_create_lf(istream) :
					i_stream_create_crlf(istream);
				pos = 0;
			}
			istream->real_stream->pos = i;
			ret1 = i_stream_read(crlf_istream);
			if (crlf_istream->real_stream->buffer_size != 0) {
				/* this is pretty evil */
				crlf_istream->real_stream->buffer_size =
					I_MAX(crlf_istream->real_stream->pos, i);
			}
			ret2 = i_stream_read(crlf_istream);
			data = i_stream_get_data(crlf_istream, &size);
			if (ret1 > 0 || ret2 > 0) {
				ret1 = I_MAX(ret1, 0) + I_MAX(ret2, 0);
				test_assert(pos + (unsigned int)ret1 == size);
				pos += ret1;
			}
			if (size > 0)
				test_assert_idx(memcmp(data, str_data(output),
							size) == 0, j*10000+i);
		}
		test_assert_idx(size == str_len(output), j*10000+i);
		i_stream_unref(&crlf_istream);
		i_stream_unref(&istream);
	}
}

void test_istream_crlf(void)
{
	const char *input[] = {
		"\rfoo",
		"foo\nbar\r\nbaz\r\r\n",
		"\r\nfoo",
		"\r\r\n",
		"\nfoo"
	};
	unsigned int i;

	test_begin("istream crlf");
	for (i = 0; i < N_ELEMENTS(input); i++)
		test_istream_crlf_input(input[i]);
	test_end();

#define ISTREAM_CRLF_TEST_REPS 1000
	test_begin("istream crlf(random)");
	for (i = 0; i < ISTREAM_CRLF_TEST_REPS; i++) T_BEGIN {
		char buf[100];
		size_t len = 0;
		while (len < sizeof(buf) - 1) {
			switch(i_rand_limit(16)) {
			case 0: goto outahere;
			case 1: buf[len] = '\r'; break;
			case 2: buf[len] = '\n'; break;
			default: buf[len]= '.'; break;
			}
			len++;
		}
	outahere:
		buf[len] = '\0';
		if (len > 0)
			test_istream_crlf_input(buf);
	} T_END;
	test_end();
}
