summaryrefslogtreecommitdiffstats
path: root/src/lib/test-istream.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/test-istream.c')
-rw-r--r--src/lib/test-istream.c381
1 files changed, 381 insertions, 0 deletions
diff --git a/src/lib/test-istream.c b/src/lib/test-istream.c
new file mode 100644
index 0000000..cedc650
--- /dev/null
+++ b/src/lib/test-istream.c
@@ -0,0 +1,381 @@
+/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "istream.h"
+#include "istream-crlf.h"
+
+static void test_istream_children(void)
+{
+ struct istream *parent, *child1, *child2;
+ const unsigned char *data;
+ size_t size;
+
+ test_begin("istream children");
+
+ parent = test_istream_create_data("123456789", 9);
+ test_istream_set_max_buffer_size(parent, 3);
+
+ child1 = i_stream_create_limit(parent, UOFF_T_MAX);
+ child2 = i_stream_create_limit(parent, UOFF_T_MAX);
+
+ /* child1 read beginning */
+ test_assert(i_stream_read(child1) == 3);
+ data = i_stream_get_data(child1, &size);
+ test_assert(size == 3 && memcmp(data, "123", 3) == 0);
+ i_stream_skip(child1, 3);
+ /* child1 read middle.. */
+ test_assert(i_stream_read(child1) == 3);
+ data = i_stream_get_data(child1, &size);
+ test_assert(size == 3 && memcmp(data, "456", 3) == 0);
+ /* child2 read beginning.. */
+ test_assert(i_stream_read(child2) == 3);
+ data = i_stream_get_data(child2, &size);
+ test_assert(size == 3 && memcmp(data, "123", 3) == 0);
+ /* child1 check middle again.. the parent has been modified,
+ so it can't return the original data (without some code changes). */
+ test_assert(i_stream_get_data_size(child1) == 0);
+ i_stream_skip(child1, 3);
+ /* child1 read end */
+ test_assert(i_stream_read(child1) == 3);
+ data = i_stream_get_data(child1, &size);
+ test_assert(size == 3 && memcmp(data, "789", 3) == 0);
+ i_stream_skip(child1, 3);
+ test_assert(i_stream_read(child1) == -1);
+ /* child2 check beginning again.. */
+ test_assert(i_stream_get_data_size(child1) == 0);
+ i_stream_skip(child2, 3);
+ /* child2 read middle */
+ test_assert(i_stream_read(child2) == 3);
+ data = i_stream_get_data(child2, &size);
+ test_assert(size == 3 && memcmp(data, "456", 3) == 0);
+ i_stream_skip(child2, 3);
+
+ i_stream_destroy(&child1);
+ i_stream_destroy(&child2);
+ i_stream_destroy(&parent);
+
+ test_end();
+}
+
+static void test_istream_next_line_expect(struct istream *is, const char *expect,
+ unsigned int i)
+{
+ const char *line = i_stream_next_line(is);
+ test_assert_strcmp_idx(line, expect, i);
+}
+
+static void test_istream_next_line(void)
+{
+ /* single line cases */
+#define TEST_CASE(a, s, b) { \
+ .input = (const unsigned char*)((a)), .input_len = sizeof((a)), \
+ .skip = s, \
+ .output = b }
+ const struct test_case_sl {
+ const unsigned char *input;
+ size_t input_len;
+ size_t skip;
+ const char *output;
+ } test_cases_sl[] = {
+ TEST_CASE("", 0, NULL),
+ TEST_CASE("a\n", 1, ""),
+ TEST_CASE("a\r\n", 0, "a"),
+ TEST_CASE("a\r\n", 1, ""),
+ TEST_CASE("a\r\n", 2, ""),
+ TEST_CASE("hello\nworld\n", 6, "world"),
+ TEST_CASE("hello\nworld", 6, NULL),
+ TEST_CASE("hello\n\n\n\n", 6, ""),
+ TEST_CASE("wrong\n\r\n\n", 0, "wrong"),
+ TEST_CASE("wrong\n\r\r\n", 6, "\r"),
+ TEST_CASE("wrong\n\r\r\n", 7, ""),
+ };
+
+ test_begin("i_stream_next_line");
+
+ for(unsigned int i = 0; i < N_ELEMENTS(test_cases_sl); i++) {
+ const struct test_case_sl *test_case = &test_cases_sl[i];
+ struct istream *input =
+ i_stream_create_copy_from_data(test_case->input, test_case->input_len);
+ test_assert_idx(i_stream_read(input) >= 0 ||
+ (input->stream_errno == 0 && input->eof), i);
+ i_stream_skip(input, test_case->skip);
+ test_assert_strcmp_idx(i_stream_next_line(input), test_case->output, i);
+ test_assert_idx(input->stream_errno == 0, i);
+ i_stream_unref(&input);
+
+ input = test_istream_create_data(test_case->input, test_case->input_len);
+ test_assert_idx(i_stream_read(input) >= 0 ||
+ (input->stream_errno == 0 && input->eof), i);
+ i_stream_skip(input, test_case->skip);
+ test_assert_strcmp_idx(i_stream_next_line(input), test_case->output, i);
+ test_assert_idx(input->stream_errno == 0, i);
+ i_stream_unref(&input);
+ }
+
+#undef TEST_CASE
+#define TEST_CASE(a) test_istream_create_data((a), sizeof(a))
+ /* multiline tests */
+ struct istream *is = TEST_CASE("\n\n\n\n\n\n");
+ size_t i;
+ test_assert(i_stream_read(is) >= 0 || (is->stream_errno == 0 && is->eof));
+ for(i = 0; i < 6; i++)
+ test_istream_next_line_expect(is, "", i);
+ test_assert(is->stream_errno == 0);
+ i_stream_unref(&is);
+
+ is = TEST_CASE(
+ "simple\r\n"
+ "multiline\n"
+ "test with\0"
+ "some exciting\n"
+ "things\r\n\0");
+ test_assert(i_stream_read(is) >= 0 || (is->stream_errno == 0 && is->eof));
+ test_istream_next_line_expect(is, "simple", 0);
+ test_istream_next_line_expect(is, "multiline", 1);
+ test_istream_next_line_expect(is, "test with", 2);
+ test_istream_next_line_expect(is, "things", 3);
+ test_istream_next_line_expect(is, NULL, 4);
+ test_assert(is->stream_errno == 0);
+ i_stream_unref(&is);
+
+ is = TEST_CASE(
+ "NUL\0"
+ "test\n");
+ test_assert(i_stream_read(is) >= 0 || (is->stream_errno == 0 && is->eof));
+ test_istream_next_line_expect(is, "NUL", 0);
+ test_istream_next_line_expect(is, NULL, 1);
+ test_assert(is->stream_errno == 0);
+ i_stream_unref(&is);
+
+ const char test_data_1[] =
+ "this is some data\n"
+ "written like this\n"
+ "to attempt and induce\n"
+ "errors or flaws\n";
+
+ is = TEST_CASE(test_data_1);
+ size_t n = 0;
+ const char *const *lines = t_strsplit(test_data_1, "\n");
+ for(i = 0; i < sizeof(test_data_1); i++) {
+ test_istream_set_size(is, i);
+ test_assert(i_stream_read(is) >= 0);
+ const char *line = i_stream_next_line(is);
+ if (line != NULL) {
+ test_assert_strcmp_idx(lines[n], line, n);
+ n++;
+ }
+ }
+ test_assert(n == 4);
+ test_assert(is->stream_errno == 0);
+ i_stream_unref(&is);
+
+ const char test_data_2[] =
+ "this is some data\n"
+ "written like this\n"
+ "to attempt and induce\n"
+ "errors or flaws";
+
+ is = TEST_CASE(test_data_2);
+ lines = t_strsplit(test_data_2, "\n");
+ i_stream_set_return_partial_line(is, TRUE);
+ n = 0;
+
+ /* requires one extra read to get the last line */
+ for(i = 0; i < sizeof(test_data_1) + 1; i++) {
+ test_istream_set_size(is, I_MIN(sizeof(test_data_1), i));
+ (void)i_stream_read(is);
+ const char *line = i_stream_next_line(is);
+ if (line != NULL) {
+ test_assert_strcmp_idx(lines[n], line, n);
+ n++;
+ }
+ (void)i_stream_read(is);
+ }
+ test_assert(n == 4);
+ test_assert(is->stream_errno == 0);
+ i_stream_unref(&is);
+
+ const char test_data_3[] =
+ "this is some data\r\n"
+ "written like this\r\n"
+ "to attempt and induce\r\n"
+ "errors or flaws\r\n";
+
+ struct istream *is_1 = TEST_CASE(test_data_3);
+ is = i_stream_create_crlf(is_1);
+ i_stream_unref(&is_1);
+
+ lines = t_strsplit_spaces(test_data_3, "\r\n");
+ n = 0;
+
+ for(i = 0; i < sizeof(test_data_3); i++) {
+ test_istream_set_size(is, i);
+ test_assert(i_stream_read(is) >= 0);
+ const char *line = i_stream_next_line(is);
+ if (line != NULL) {
+ test_assert_strcmp_idx(lines[n], line, n);
+ n++;
+ }
+ }
+ test_assert(n == 4);
+ test_assert(is->stream_errno == 0);
+ i_stream_unref(&is);
+
+ test_end();
+}
+
+static void test_istream_read_next_line(void)
+{
+ /* single line cases */
+#undef TEST_CASE
+#define TEST_CASE(a, s, b) { \
+ .input = (const unsigned char*)((a)), .input_len = sizeof((a)), \
+ .skip = s, \
+ .output = b }
+ const struct test_case_sl {
+ const unsigned char *input;
+ size_t input_len;
+ size_t skip;
+ const char *output;
+ } test_cases_sl[] = {
+ TEST_CASE("", 0, NULL),
+ TEST_CASE("a\n", 1, ""),
+ TEST_CASE("a\r\n", 0, "a"),
+ TEST_CASE("a\r\n", 1, ""),
+ TEST_CASE("a\r\n", 2, ""),
+ TEST_CASE("hello\nworld\n", 6, "world"),
+ TEST_CASE("hello\nworld", 6, NULL),
+ TEST_CASE("hello\n\n\n\n", 6, ""),
+ TEST_CASE("wrong\n\r\n\n", 0, "wrong"),
+ TEST_CASE("wrong\n\r\r\n", 6, "\r"),
+ TEST_CASE("wrong\n\r\r\n", 7, ""),
+ };
+
+ test_begin("i_stream_read_next_line");
+ for(unsigned int i = 0; i < N_ELEMENTS(test_cases_sl); i++) {
+ const struct test_case_sl *test_case = &test_cases_sl[i];
+ struct istream *input =
+ i_stream_create_copy_from_data(test_case->input, test_case->input_len);
+ i_stream_skip(input, test_case->skip);
+ test_assert_strcmp_idx(i_stream_read_next_line(input), test_case->output, i);
+ test_assert_idx(input->stream_errno == 0, i);
+ i_stream_unref(&input);
+
+ input = test_istream_create_data(test_case->input, test_case->input_len);
+ i_stream_skip(input, test_case->skip);
+ test_assert_strcmp_idx(i_stream_read_next_line(input), test_case->output, i);
+ test_assert_idx(input->stream_errno == 0, i);
+ i_stream_unref(&input);
+ }
+
+ const char test_data_1[] =
+ "this is some data\n"
+ "written like this\n"
+ "to attempt and induce\n"
+ "errors or flaws\n";
+
+#undef TEST_CASE
+#define TEST_CASE(a) test_istream_create_data((a), sizeof(a))
+ /* multiline tests */
+ struct istream *is = TEST_CASE("\n\n\n\n\n\n");
+ size_t i;
+ for(i = 0; i < 6; i++)
+ test_assert_strcmp_idx(i_stream_read_next_line(is), "", i);
+ test_assert(is->stream_errno == 0);
+ i_stream_unref(&is);
+
+ is = TEST_CASE(
+ "simple\r\n"
+ "multiline\n"
+ "test with\0"
+ "some exciting\n"
+ "things\r\n\0");
+ test_assert_strcmp_idx(i_stream_read_next_line(is), "simple", 0);
+ test_assert_strcmp_idx(i_stream_read_next_line(is), "multiline", 1);
+ test_assert_strcmp_idx(i_stream_read_next_line(is), "test with", 2);
+ test_assert_strcmp_idx(i_stream_read_next_line(is), "things", 3);
+ test_assert_strcmp_idx(i_stream_read_next_line(is), NULL, 4);
+ test_assert(is->stream_errno == 0);
+ i_stream_unref(&is);
+
+ is = TEST_CASE(
+ "NUL\0"
+ "test\n");
+ test_assert_strcmp_idx(i_stream_read_next_line(is), "NUL", 0);
+ test_assert_strcmp_idx(i_stream_read_next_line(is), NULL, 1);
+ test_assert(is->stream_errno == 0);
+ i_stream_unref(&is);
+
+ is = TEST_CASE(test_data_1);
+ size_t n = 0;
+ const char *const *lines = t_strsplit(test_data_1, "\n");
+ for(i = 0; i < sizeof(test_data_1); i++) {
+ test_istream_set_size(is, i);
+ const char *line = i_stream_read_next_line(is);
+ if (line != NULL) {
+ test_assert_strcmp_idx(lines[n], line, n);
+ n++;
+ }
+ }
+ test_assert(n == 4);
+ test_assert(is->stream_errno == 0);
+ i_stream_unref(&is);
+
+ const char test_data_2[] =
+ "this is some data\n"
+ "written like this\n"
+ "to attempt and induce\n"
+ "errors or flaws";
+
+ is = TEST_CASE(test_data_2);
+ lines = t_strsplit(test_data_2, "\n");
+ i_stream_set_return_partial_line(is, TRUE);
+ n = 0;
+
+ for(i = 0; i < sizeof(test_data_1); i++) {
+ test_istream_set_size(is, i);
+ const char *line = i_stream_read_next_line(is);
+ if (line != NULL) {
+ test_assert_strcmp_idx(lines[n], line, n);
+ n++;
+ }
+ }
+ test_assert(n == 4);
+ test_assert(is->stream_errno == 0);
+ i_stream_unref(&is);
+
+ const char test_data_3[] =
+ "this is some data\r\n"
+ "written like this\r\n"
+ "to attempt and induce\r\n"
+ "errors or flaws\r\n";
+
+
+ struct istream *is_1 = TEST_CASE(test_data_3);
+ is = i_stream_create_crlf(is_1);
+ i_stream_unref(&is_1);
+
+ lines = t_strsplit_spaces(test_data_3, "\r\n");
+ n = 0;
+
+ for(i = 0; i < sizeof(test_data_3); i++) {
+ test_istream_set_size(is, i);
+ const char *line = i_stream_read_next_line(is);
+ if (line != NULL) {
+ test_assert_strcmp_idx(lines[n], line, n);
+ n++;
+ }
+ }
+ test_assert(n == 4);
+ test_assert(is->stream_errno == 0);
+ i_stream_unref(&is);
+
+ test_end();
+}
+
+void test_istream(void)
+{
+ test_istream_children();
+ test_istream_next_line();
+ test_istream_read_next_line();
+}