diff options
Diffstat (limited to 'src/lib/test-istream.c')
-rw-r--r-- | src/lib/test-istream.c | 381 |
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(); +} |