# HG changeset patch # User Timo Sirainen # Date 1241900441 14400 # Node ID 665ea7a8d26e5cb81568bd464d4400622d6fe67b # Parent 2090983d37fa5f43404faf465a79fb830c03cdc4 Tests are now run on "make check". Added initial tests for lib-index. Moved old tests away from tests/ to libraries' individual directories. diff -r 2090983d37fa -r 665ea7a8d26e .hgignore --- a/.hgignore Sat May 09 16:10:36 2009 -0400 +++ b/.hgignore Sat May 09 16:20:41 2009 -0400 @@ -61,9 +61,13 @@ src/dict/dict src/imap-login/imap-login src/imap/imap +src/lib/test-lib src/lib/unicodemap.c src/lib/UnicodeData.txt src/lib-dict/dict-drivers-register.c +src/lib-index/test-index +src/lib-imap/test-imap +src/lib-mail/test-mail src/lib-sql/sql-drivers-register.c src/lib-storage/register/mail-storage-register.c src/lib-storage/register/mailbox-list-register.c @@ -76,9 +80,6 @@ src/plugins/fts-squat/squat-test src/pop3-login/pop3-login src/pop3/pop3 -src/tests/test-lib -src/tests/test-mail -src/tests/test-imap src/util/doveadm src/util/dovecotpw src/util/gdbhelper diff -r 2090983d37fa -r 665ea7a8d26e configure.in --- a/configure.in Sat May 09 16:10:36 2009 -0400 +++ b/configure.in Sat May 09 16:20:41 2009 -0400 @@ -2442,6 +2442,7 @@ src/lib-otp/Makefile src/lib-dovecot/Makefile src/lib-settings/Makefile +src/lib-test/Makefile src/lib-storage/Makefile src/lib-storage/list/Makefile src/lib-storage/index/Makefile @@ -2465,7 +2466,6 @@ src/master/Makefile src/pop3/Makefile src/pop3-login/Makefile -src/tests/Makefile src/util/Makefile src/plugins/Makefile src/plugins/acl/Makefile diff -r 2090983d37fa -r 665ea7a8d26e src/Makefile.am --- a/src/Makefile.am Sat May 09 16:10:36 2009 -0400 +++ b/src/Makefile.am Sat May 09 16:20:41 2009 -0400 @@ -9,6 +9,7 @@ lib-settings SUBDIRS = \ + lib-test \ $(LIBDOVECOT_SUBDIRS) \ lib-dovecot \ lib-index \ @@ -30,6 +31,10 @@ lmtp \ log \ config \ - tests \ util \ plugins + +test: + for dir in $(SUBDIRS); do \ + cd $$dir && if ! $(MAKE) test; then exit 1; fi; cd ..; \ + done diff -r 2090983d37fa -r 665ea7a8d26e src/lib-imap/Makefile.am --- a/src/lib-imap/Makefile.am Sat May 09 16:10:36 2009 -0400 +++ b/src/lib-imap/Makefile.am Sat May 09 16:20:41 2009 -0400 @@ -2,6 +2,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-charset \ -I$(top_srcdir)/src/lib-mail @@ -38,3 +39,23 @@ else noinst_HEADERS = $(headers) endif + +test_programs = test-imap +noinst_PROGRAMS = $(test_programs) + +test_libs = \ + libimap.la \ + ../lib-test/libtest.la \ + ../lib/liblib.la + +test_imap_SOURCES = \ + test-imap.c + +test_imap_LDADD = $(test_libs) +test_imap_DEPENDENCIES = $(test_libs) + +check: check-am check-test +check-test: $(test_programs) + for bin in $(test_programs); do \ + if ! ./$$bin; then exit 1; fi \ + done diff -r 2090983d37fa -r 665ea7a8d26e src/lib-imap/test-imap.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-imap/test-imap.c Sat May 09 16:20:41 2009 -0400 @@ -0,0 +1,178 @@ +/* Copyright (c) 2008-2009 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "unichar.h" +#include "imap-match.h" +#include "imap-utf7.h" +#include "test-common.h" + +struct test_imap_match { + const char *pattern; + const char *input; + enum imap_match_result result; +}; + +static void test_imap_match(void) +{ + struct test_imap_match test[] = { + { "", "", IMAP_MATCH_YES }, + { "a", "b", IMAP_MATCH_NO }, + { "foo", "foo", IMAP_MATCH_YES }, + { "foo", "foo/", IMAP_MATCH_PARENT }, + { "%", "", IMAP_MATCH_YES }, + { "%", "foo", IMAP_MATCH_YES }, + { "%", "foo/", IMAP_MATCH_PARENT }, + { "%/", "foo/", IMAP_MATCH_YES }, + { "%", "foo/bar", IMAP_MATCH_PARENT }, + { "%/%", "foo", IMAP_MATCH_CHILDREN }, + { "%/%", "foo/", IMAP_MATCH_YES }, + { "foo/bar/%", "foo", IMAP_MATCH_CHILDREN }, + { "foo/bar/%", "foo/", IMAP_MATCH_CHILDREN }, + { "foo*", "foo", IMAP_MATCH_YES }, + { "foo*", "foo/", IMAP_MATCH_YES }, + { "foo*", "fobo", IMAP_MATCH_NO }, + { "*foo*", "bar/foo/", IMAP_MATCH_YES }, + { "*foo*", "fobo", IMAP_MATCH_CHILDREN }, + { "foo*bar", "foobar/baz", IMAP_MATCH_CHILDREN | IMAP_MATCH_PARENT }, + { "*foo*", "fobo", IMAP_MATCH_CHILDREN }, + { "%/%/%", "foo/", IMAP_MATCH_CHILDREN }, + { "%/%o/%", "foo/", IMAP_MATCH_CHILDREN }, + { "%/%o/%", "foo", IMAP_MATCH_CHILDREN }, + { "inbox", "inbox", IMAP_MATCH_YES }, + { "inbox", "INBOX", IMAP_MATCH_NO } + }; + struct test_imap_match inbox_test[] = { + { "inbox", "inbox", IMAP_MATCH_YES }, + { "inbox", "iNbOx", IMAP_MATCH_YES }, + { "i%X", "iNbOx", IMAP_MATCH_YES }, + { "%I%N%B%O%X%", "inbox", IMAP_MATCH_YES }, + { "i%X/foo", "iNbOx/foo", IMAP_MATCH_YES }, + { "%I%N%B%O%X%/foo", "inbox/foo", IMAP_MATCH_YES }, + { "i%X/foo", "inbx/foo", IMAP_MATCH_NO } + }; + struct imap_match_glob *glob; + unsigned int i; + enum imap_match_result result; + + /* first try tests without inboxcasing */ + for (i = 0; i < N_ELEMENTS(test); i++) { + glob = imap_match_init(default_pool, test[i].pattern, + FALSE, '/'); + result = imap_match(glob, test[i].input); + imap_match_deinit(&glob); + + test_out(t_strdup_printf("imap_match(%d)", i), + result == test[i].result); + } + + /* inboxcasing tests */ + for (i = 0; i < N_ELEMENTS(inbox_test); i++) { + glob = imap_match_init(default_pool, inbox_test[i].pattern, + TRUE, '/'); + result = imap_match(glob, inbox_test[i].input); + imap_match_deinit(&glob); + + test_out(t_strdup_printf("imap_match(inboxcase, %d)", i), + result == inbox_test[i].result); + } +} + +static void test_imap_utf7(void) +{ + static const char *to_utf7[] = { + "&&x&&", "&-&-x&-&-", + "~peter/mail/台北/日本語", "~peter/mail/&U,BTFw-/&ZeVnLIqe-", + "tietäjä", "tiet&AOQ-j&AOQ-", + "p", NULL, + NULL + }; + static const char *invalid_utf7[] = { + "&Jjo!", + "&U,BTFw-&ZeVnLIqe-", + NULL + }; + string_t *src, *dest; + const char *orig_src; + unsigned int i, j; + unichar_t chr; + bool success, all_success = TRUE; + + src = t_str_new(256); + dest = t_str_new(256); + + for (i = 0; to_utf7[i] != NULL; i += 2) { + str_truncate(dest, 0); + if (imap_utf8_to_utf7(to_utf7[i], dest) < 0) + success = to_utf7[i+1] == NULL; + else { + success = to_utf7[i+1] != NULL && + strcmp(to_utf7[i+1], str_c(dest)) == 0; + } + if (!success) { + test_out(t_strdup_printf("imap_utf8_to_utf7(%d)", i/2), + FALSE); + all_success = FALSE; + } else if (to_utf7[i+1] != NULL) { + str_truncate(dest, 0); + if (imap_utf7_to_utf8(to_utf7[i+1], dest) < 0 || + strcmp(to_utf7[i], str_c(dest)) != 0) { + test_out(t_strdup_printf("imap_utf7_to_utf8(%d)", i/2), + FALSE); + all_success = FALSE; + } + } + } + if (all_success) + test_out("imap_utf8_to_utf7()", TRUE); + + success = TRUE; + for (chr = 0xffff; chr <= 0x10010; chr++) { + for (i = 1; i <= 10; i++) { + str_truncate(src, 0); + str_truncate(dest, 0); + for (j = 0; j < i; j++) { + if (j % 3 == 0) + str_append_c(src, 'x'); + if (j % 5 == 0) + str_append_c(src, '&'); + uni_ucs4_to_utf8_c(chr, src); + } + + orig_src = t_strdup(str_c(src)); + str_truncate(src, 0); + + if (imap_utf8_to_utf7(orig_src, dest) < 0) + success = FALSE; + else if (imap_utf7_to_utf8(str_c(dest), src) < 0) + success = FALSE; + else + success = strcmp(str_c(src), orig_src) == 0; + if (!success) + goto end; + } + } +end: + test_out("imap_utf7_to_utf8(reverse)", success); + for (i = 0; invalid_utf7[i] != NULL; i++) { + str_truncate(dest, 0); + if (imap_utf7_to_utf8(invalid_utf7[i], dest) == 0) { + test_out(t_strdup_printf("imap_utf7_to_utf8(invalid.%d)", i), + FALSE); + all_success = FALSE; + } + } + if (all_success) + test_out("imap_utf7_to_utf8(invalid)", TRUE); +} + +int main(void) +{ + static void (*test_functions[])(void) = { + test_imap_match, + test_imap_utf7, + + NULL + }; + return test_run(test_functions); +} diff -r 2090983d37fa -r 665ea7a8d26e src/lib-index/Makefile.am --- a/src/lib-index/Makefile.am Sat May 09 16:10:36 2009 -0400 +++ b/src/lib-index/Makefile.am Sat May 09 16:20:41 2009 -0400 @@ -2,6 +2,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-mail libindex_la_SOURCES = \ @@ -54,9 +55,34 @@ mailbox-list-index.h \ mailbox-list-index-private.h +test_programs = test-index +noinst_PROGRAMS = $(test_programs) + +test_libs = \ + libindex.la \ + ../lib-test/libtest.la \ + ../lib-mail/libmail.la \ + ../lib/liblib.la + +test_index_SOURCES = \ + test-index.c \ + test-transaction-log-view.c + +test_headers = \ + test-index.h + +test_index_LDADD = $(test_libs) +test_index_DEPENDENCIES = $(test_libs) + +check: check-am check-test +check-test: $(test_programs) + for bin in $(test_programs); do \ + if ! ./$$bin; then exit 1; fi \ + done + if INSTALL_HEADERS pkginc_libdir=$(pkgincludedir) - pkginc_lib_HEADERS = $(headers) + pkginc_lib_HEADERS = $(headers) $(test_headers) else - noinst_HEADERS = $(headers) + noinst_HEADERS = $(headers) $(test_headers) endif diff -r 2090983d37fa -r 665ea7a8d26e src/lib-index/mail-index-transaction-export.c --- a/src/lib-index/mail-index-transaction-export.c Sat May 09 16:10:36 2009 -0400 +++ b/src/lib-index/mail-index-transaction-export.c Sat May 09 16:20:41 2009 -0400 @@ -16,7 +16,8 @@ log_append_buffer(struct mail_index_export_context *ctx, const buffer_t *buf, enum mail_transaction_type type) { - mail_transaction_log_append_add(ctx->append_ctx, type, buf); + mail_transaction_log_append_add(ctx->append_ctx, type, + buf->data, buf->used); } static const buffer_t * diff -r 2090983d37fa -r 665ea7a8d26e src/lib-index/mail-index-transaction.c --- a/src/lib-index/mail-index-transaction.c Sat May 09 16:10:36 2009 -0400 +++ b/src/lib-index/mail-index-transaction.c Sat May 09 16:20:41 2009 -0400 @@ -267,12 +267,13 @@ static int mail_index_transaction_commit_real(struct mail_index_transaction *t) { struct mail_transaction_log *log = t->view->index->log; + bool external = (t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0; struct mail_transaction_log_append_ctx *ctx; uint32_t log_seq1, log_seq2; uoff_t log_offset1, log_offset2; int ret; - if (mail_transaction_log_append_begin(t, &ctx) < 0) + if (mail_transaction_log_append_begin(log->index, external, &ctx) < 0) return -1; ret = mail_transaction_log_file_refresh(t, ctx); if (ret > 0) { diff -r 2090983d37fa -r 665ea7a8d26e src/lib-index/mail-transaction-log-append.c --- a/src/lib-index/mail-transaction-log-append.c Sat May 09 16:10:36 2009 -0400 +++ b/src/lib-index/mail-transaction-log-append.c Sat May 09 16:20:41 2009 -0400 @@ -8,14 +8,14 @@ void mail_transaction_log_append_add(struct mail_transaction_log_append_ctx *ctx, enum mail_transaction_type type, - const buffer_t *buf) + const void *data, size_t size) { struct mail_transaction_header hdr; i_assert((type & MAIL_TRANSACTION_TYPE_MASK) != 0); - i_assert((buf->used % 4) == 0); + i_assert((size % 4) == 0); - if (buf->used == 0) + if (size == 0) return; memset(&hdr, 0, sizeof(hdr)); @@ -24,15 +24,15 @@ hdr.type |= MAIL_TRANSACTION_EXPUNGE_PROT; if (ctx->external) hdr.type |= MAIL_TRANSACTION_EXTERNAL; - hdr.size = sizeof(hdr) + buf->used; + hdr.size = sizeof(hdr) + size; hdr.size = mail_index_uint32_to_offset(hdr.size); buffer_append(ctx->output, &hdr, sizeof(hdr)); - buffer_append(ctx->output, buf->data, buf->used); + buffer_append(ctx->output, data, size); - if (mail_transaction_header_has_modseq(buf->data, - CONST_PTR_OFFSET(buf->data, sizeof(hdr)), - ctx->new_highest_modseq)) + if (mail_transaction_header_has_modseq(data, + CONST_PTR_OFFSET(data, sizeof(hdr)), + ctx->new_highest_modseq)) ctx->new_highest_modseq++; } @@ -158,7 +158,7 @@ buffer_append(buf, &offset, sizeof(offset)); mail_transaction_log_append_add(ctx, MAIL_TRANSACTION_HEADER_UPDATE, - buf); + buf->data, buf->used); } static int @@ -189,13 +189,11 @@ return 0; } -int mail_transaction_log_append_begin(struct mail_index_transaction *t, +int mail_transaction_log_append_begin(struct mail_index *index, bool external, struct mail_transaction_log_append_ctx **ctx_r) { struct mail_transaction_log_append_ctx *ctx; - struct mail_index *index; - index = mail_index_view_get_index(t->view); if (!index->log_locked) { if (mail_transaction_log_lock_head(index->log) < 0) return -1; @@ -203,7 +201,7 @@ ctx = i_new(struct mail_transaction_log_append_ctx, 1); ctx->log = index->log; ctx->output = buffer_create_dynamic(default_pool, 1024); - ctx->external = (t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0; + ctx->external = external; *ctx_r = ctx; return 0; diff -r 2090983d37fa -r 665ea7a8d26e src/lib-index/mail-transaction-log.h --- a/src/lib-index/mail-transaction-log.h Sat May 09 16:10:36 2009 -0400 +++ b/src/lib-index/mail-transaction-log.h Sat May 09 16:20:41 2009 -0400 @@ -224,11 +224,11 @@ void mail_transaction_log_views_close(struct mail_transaction_log *log); -int mail_transaction_log_append_begin(struct mail_index_transaction *t, +int mail_transaction_log_append_begin(struct mail_index *index, bool external, struct mail_transaction_log_append_ctx **ctx_r); void mail_transaction_log_append_add(struct mail_transaction_log_append_ctx *ctx, enum mail_transaction_type type, - const buffer_t *buf); + const void *data, size_t size); int mail_transaction_log_append_commit(struct mail_transaction_log_append_ctx **ctx); /* Lock transaction log for index synchronization. Log cannot be read or diff -r 2090983d37fa -r 665ea7a8d26e src/lib-index/test-index.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-index/test-index.c Sat May 09 16:20:41 2009 -0400 @@ -0,0 +1,12 @@ +/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */ + +#include "test-index.h" + +int main(void) +{ + static void (*test_functions[])(void) = { + test_transaction_log_view, + NULL + }; + return test_run(test_functions); +} diff -r 2090983d37fa -r 665ea7a8d26e src/lib-index/test-index.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-index/test-index.h Sat May 09 16:20:41 2009 -0400 @@ -0,0 +1,9 @@ +#ifndef TEST_INDEX_H +#define TEST_INDEX_H + +#include "lib.h" +#include "test-common.h" + +void test_transaction_log_view(void); + +#endif diff -r 2090983d37fa -r 665ea7a8d26e src/lib-index/test-transaction-log-view.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-index/test-transaction-log-view.c Sat May 09 16:20:41 2009 -0400 @@ -0,0 +1,148 @@ +/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */ + +#include "test-index.h" +#include "array.h" +#include "mail-index-private.h" +#include "mail-transaction-log-view-private.h" + +static struct mail_transaction_log *log; +static struct mail_transaction_log_view *view; + +static void +test_transaction_log_file_add(uint32_t file_seq) +{ + struct mail_transaction_log_file **p, *file; + + file = i_new(struct mail_transaction_log_file, 1); + file->hdr.file_seq = file_seq; + file->hdr.hdr_size = file->sync_offset = sizeof(file->hdr); + file->hdr.prev_file_seq = file_seq - 1; + file->hdr.indexid = 1; + file->log = log; + file->fd = -1; + + /* files must be sorted by file_seq */ + for (p = &log->files; *p != NULL; p = &(*p)->next) { + if ((*p)->hdr.file_seq > file->hdr.file_seq) + break; + i_assert((*p)->hdr.file_seq < file->hdr.file_seq); + } + *p = file; + log->head = file; +} + +static bool view_is_file_refed(uint32_t file_seq) +{ + struct mail_transaction_log_file *const *files; + unsigned int i, count; + bool ret = FALSE; + + files = array_get(&view->file_refs, &count); + for (i = 0; i < count; i++) { + if (files[i]->hdr.file_seq == file_seq) { + i_assert(!ret); /* could be a test too.. */ + ret = TRUE; + } + } + return ret; +} + +void test_transaction_log_view(void) +{ + const struct mail_transaction_header *hdr; + struct mail_transaction_log_append_ctx *append_ctx; + struct mail_index_record append_rec; + const struct mail_index_record *rec; + const void *data; + uint32_t seq; + uoff_t offset, last_log_size; + bool reset; + + test_begin("init"); + log = i_new(struct mail_transaction_log, 1); + log->index = i_new(struct mail_index, 1); + log->index->log = log; + log->index->log_locked = TRUE; + test_transaction_log_file_add(1); + test_transaction_log_file_add(2); + test_transaction_log_file_add(3); + + /* add an append record to the 3rd log file */ + memset(&append_rec, 0, sizeof(append_rec)); + append_rec.uid = 1; + test_assert(mail_transaction_log_append_begin(log->index, TRUE, &append_ctx) == 0); + mail_transaction_log_append_add(append_ctx, MAIL_TRANSACTION_APPEND, + &append_rec, sizeof(append_rec)); + test_assert(mail_transaction_log_append_commit(&append_ctx) == 0); + last_log_size = sizeof(struct mail_transaction_log_header) + + sizeof(struct mail_transaction_header) + sizeof(append_rec); + + view = mail_transaction_log_view_open(log); + test_assert(view != NULL && log->views == view && + !view_is_file_refed(1) && !view_is_file_refed(2) && + view_is_file_refed(3)); + test_end(); + + /* we have files 1-3 opened */ + test_begin("set all"); + test_assert(mail_transaction_log_view_set(view, 0, 0, (uint32_t)-1, (uoff_t)-1, &reset) == 1 && + reset && view_is_file_refed(1) && view_is_file_refed(2) && + view_is_file_refed(3) && + !mail_transaction_log_view_is_corrupted(view)); + mail_transaction_log_view_get_prev_pos(view, &seq, &offset); + test_assert(seq == 1 && offset == sizeof(struct mail_transaction_log_header)); + test_assert(mail_transaction_log_view_next(view, &hdr, &data) == 1); + test_assert(hdr->type == (MAIL_TRANSACTION_APPEND | MAIL_TRANSACTION_EXTERNAL)); + rec = data; + test_assert(memcmp(rec, &append_rec, sizeof(*rec)) == 0); + test_assert(mail_transaction_log_view_next(view, &hdr, &data) == 0); + test_assert(mail_transaction_log_view_is_last(view)); + mail_transaction_log_view_get_prev_pos(view, &seq, &offset); + test_assert(seq == 3 && offset == last_log_size); + test_end(); + + test_begin("set first"); + test_assert(mail_transaction_log_view_set(view, 0, 0, 0, 0, &reset) == 1); + mail_transaction_log_view_get_prev_pos(view, &seq, &offset); + test_assert(seq == 1 && offset == sizeof(struct mail_transaction_log_header)); + test_assert(mail_transaction_log_view_next(view, &hdr, &data) == 0); + mail_transaction_log_view_get_prev_pos(view, &seq, &offset); + test_assert(seq == 1 && offset == sizeof(struct mail_transaction_log_header)); + test_end(); + + test_begin("set end"); + test_assert(mail_transaction_log_view_set(view, 3, last_log_size, (uint32_t)-1, (uoff_t)-1, &reset) == 1); + mail_transaction_log_view_get_prev_pos(view, &seq, &offset); + test_assert(seq == 3 && offset == last_log_size); + test_assert(mail_transaction_log_view_next(view, &hdr, &data) == 0); + mail_transaction_log_view_get_prev_pos(view, &seq, &offset); + test_assert(seq == 3 && offset == last_log_size); + test_end(); + + test_begin("log clear"); + mail_transaction_log_view_clear(view, 2); + test_assert(!view_is_file_refed(1) && view_is_file_refed(2) && + view_is_file_refed(3)); + test_end(); + + /* --- first file has been removed --- */ + + test_begin("removed first"); + mail_transaction_logs_clean(log); + test_assert(log->files->hdr.file_seq == 2); + test_end(); + + test_begin("set 2-3"); + test_assert(mail_transaction_log_view_set(view, 2, 0, (uint32_t)-1, (uoff_t)-1, &reset) == 1); + test_end(); + + test_begin("missing log handing"); + test_assert(mail_transaction_log_view_set(view, 0, 0, (uint32_t)-1, (uoff_t)-1, &reset) == 0); + test_end(); + + test_begin("closed log handling"); + view->log = NULL; + test_assert(mail_transaction_log_view_set(view, 0, 0, (uint32_t)-1, (uoff_t)-1, &reset) == -1); + view->log = log; + test_end(); +} diff -r 2090983d37fa -r 665ea7a8d26e src/lib-mail/Makefile.am --- a/src/lib-mail/Makefile.am Sat May 09 16:10:36 2009 -0400 +++ b/src/lib-mail/Makefile.am Sat May 09 16:20:41 2009 -0400 @@ -2,6 +2,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-test \ -I$(top_srcdir)/src/lib-charset libmail_la_SOURCES = \ @@ -47,3 +48,23 @@ else noinst_HEADERS = $(headers) endif + +test_programs = test-mail +noinst_PROGRAMS = $(test_programs) + +test_libs = \ + libmail.la \ + ../lib-test/libtest.la \ + ../lib/liblib.la + +test_mail_SOURCES = \ + test-mail.c + +test_mail_LDADD = $(test_libs) +test_mail_DEPENDENCIES = $(test_libs) + +check: check-am check-test +check-test: $(test_programs) + for bin in $(test_programs); do \ + if ! ./$$bin; then exit 1; fi \ + done diff -r 2090983d37fa -r 665ea7a8d26e src/lib-mail/test-mail.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-mail/test-mail.c Sat May 09 16:20:41 2009 -0400 @@ -0,0 +1,369 @@ +/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "istream.h" +#include "rfc822-parser.h" +#include "rfc2231-parser.h" +#include "message-address.h" +#include "message-date.h" +#include "message-parser.h" +#include "istream-header-filter.h" +#include "test-common.h" + +static const char test_msg[] = +"Return-Path: \n" +"Subject: Hello world\n" +"From: Test User \n" +"To: Another User \n" +"Message-Id: <1.2.3.4@example>\n" +"Mime-Version: 1.0\n" +"Date: Sun, 23 May 2007 04:58:08 +0300\n" +"Content-Type: multipart/signed; micalg=pgp-sha1;\n" +" protocol=\"application/pgp-signature\";\n" +" boundary=\"=-GNQXLhuj24Pl1aCkk4/d\"\n" +"\n" +"--=-GNQXLhuj24Pl1aCkk4/d\n" +"Content-Type: text/plain\n" +"Content-Transfer-Encoding: quoted-printable\n" +"\n" +"There was a day=20\n" +"a happy=20day\n" +"\n" +"--=-GNQXLhuj24Pl1aCkk4/d\n" +"Content-Type: application/pgp-signature; name=signature.asc\n" +"\n" +"-----BEGIN PGP SIGNATURE-----\n" +"Version: GnuPG v1.2.4 (GNU/Linux)\n" +"\n" +"invalid\n" +"-----END PGP SIGNATURE-----\n" +"\n" +"--=-GNQXLhuj24Pl1aCkk4/d--\n" +"\n" +"\n"; +#define TEST_MSG_LEN (sizeof(test_msg)-1) + +static bool cmp_addr(const struct message_address *a1, + const struct message_address *a2) +{ + return null_strcmp(a1->name, a2->name) == 0 && + null_strcmp(a1->route, a2->route) == 0 && + null_strcmp(a1->mailbox, a2->mailbox) == 0 && + null_strcmp(a1->domain, a2->domain) == 0 && + a1->invalid_syntax == a2->invalid_syntax; +} + +static void test_message_address(void) +{ + static const char *input[] = { + "user@domain", + "", + "foo bar ", + "\"foo bar\" ", + "<@route:user@domain>", + "<@route@route2:user@domain>", + "hello <@route ,@route2:user@domain>", + "user (hello)", + "hello ", + "@domain" + }; + static struct message_address group_prefix = { + NULL, NULL, NULL, "group", NULL, FALSE + }; + static struct message_address group_suffix = { + NULL, NULL, NULL, NULL, NULL, FALSE + }; + static struct message_address output[] = { + { NULL, NULL, NULL, "user", "domain", FALSE }, + { NULL, NULL, NULL, "user", "domain", FALSE }, + { NULL, "foo bar", NULL, "user", "domain", FALSE }, + { NULL, "foo bar", NULL, "user", "domain", FALSE }, + { NULL, NULL, "@route", "user", "domain", FALSE }, + { NULL, NULL, "@route,@route2", "user", "domain", FALSE }, + { NULL, "hello", "@route,@route2", "user", "domain", FALSE }, + { NULL, "hello", NULL, "user", "", TRUE }, + { NULL, "hello", NULL, "user", "", TRUE }, + { NULL, NULL, NULL, "", "domain", TRUE } + }; + struct message_address *addr; + string_t *group; + unsigned int i; + bool success; + + group = t_str_new(256); + str_append(group, "group: "); + + for (i = 0; i < N_ELEMENTS(input); i++) { + addr = message_address_parse(pool_datastack_create(), + (const unsigned char *)input[i], + strlen(input[i]), -1U, FALSE); + success = addr != NULL && addr->next == NULL && + cmp_addr(addr, &output[i]); + test_out(t_strdup_printf("message_address_parse(%d)", i), + success); + + if (!output[i].invalid_syntax) { + if (i != 0) { + if ((i % 2) == 0) + str_append(group, ","); + else + str_append(group, " , \n "); + } + str_append(group, input[i]); + } + } + str_append_c(group, ';'); + + addr = message_address_parse(pool_datastack_create(), str_data(group), + str_len(group), -1U, FALSE); + success = addr != NULL && cmp_addr(addr, &group_prefix); + addr = addr->next; + for (i = 0; i < N_ELEMENTS(input) && addr != NULL; i++) { + if (output[i].invalid_syntax) + continue; + if (!cmp_addr(addr, &output[i])) { + success = FALSE; + break; + } + addr = addr->next; + } + if (addr == NULL || addr->next != NULL || + !cmp_addr(addr, &group_suffix)) + success = FALSE; + test_out("message_address_parse(group)", success); +} + +struct test_message_date_output { + time_t time; + int tz_offset; + bool ret; +}; + +static void test_message_date_parse(void) +{ + static const char *input[] = { +#ifdef TIME_T_SIGNED + "Thu, 01 Jan 1970 01:59:59 +0200", + "Fri, 13 Dec 1901 20:45:53 +0000", +#endif +#if (TIME_T_MAX_BITS > 32 || !defined(TIME_T_SIGNED)) + "Sun, 07 Feb 2106 06:28:15 +0000", +#endif + "Wed, 07 Nov 2007 01:07:20 +0200", + "Wed, 07 Nov 2007 01:07:20", + "Thu, 01 Jan 1970 02:00:00 +0200", + "Tue, 19 Jan 2038 03:14:07 +0000", + "Tue, 19 Jan 2038" + }; + static struct test_message_date_output output[] = { +#ifdef TIME_T_SIGNED + { -1, 2*60, TRUE }, + { -2147483647, 0, TRUE }, +#endif +#if (TIME_T_MAX_BITS > 32 || !defined(TIME_T_SIGNED)) + { 4294967295, 0, TRUE }, +#endif + { 1194390440, 2*60, TRUE }, + { 1194397640, 0, TRUE }, + { 0, 2*60, TRUE }, + { 2147483647, 0, TRUE }, + { 0, 0, FALSE } + }; + unsigned int i; + bool success; + time_t t; + int tz; + bool ret; + + for (i = 0; i < N_ELEMENTS(input); i++) { + ret = message_date_parse((const unsigned char *)input[i], + strlen(input[i]), &t, &tz); + success = (!ret && !output[i].ret) || + (ret == output[i].ret && t == output[i].time && + tz == output[i].tz_offset); + test_out(t_strdup_printf("message_date_parse(%d)", i), success); + } +} + +static bool msg_parts_cmp(struct message_part *p1, struct message_part *p2) +{ + while (p1 != NULL || p2 != NULL) { + if ((p1 != NULL) != (p2 != NULL)) + return FALSE; + if ((p1->children != NULL) != (p2->children != NULL)) + return FALSE; + + if (p1->children) { + if (!msg_parts_cmp(p1->children, p2->children)) + return FALSE; + } + + if (p1->physical_pos != p2->physical_pos || + p1->header_size.physical_size != p2->header_size.physical_size || + p1->header_size.virtual_size != p2->header_size.virtual_size || + p1->header_size.lines != p2->header_size.lines || + p1->body_size.physical_size != p2->body_size.physical_size || + p1->body_size.virtual_size != p2->body_size.virtual_size || + p1->body_size.lines != p2->body_size.lines || + p1->flags != p2->flags) + return FALSE; + + p1 = p1->next; + p2 = p2->next; + } + return TRUE; +} + +static void test_message_parser(void) +{ + struct message_parser_ctx *parser; + struct istream *input; + struct message_part *parts, *parts2; + struct message_block block; + unsigned int i; + bool success = TRUE; + pool_t pool; + int ret; + + pool = pool_alloconly_create("message parser", 10240); + input = i_stream_create_from_data(test_msg, TEST_MSG_LEN); + + parser = message_parser_init(pool, input, 0, 0); + while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; + i_assert(ret < 0); + ret = message_parser_deinit(&parser, &parts); + i_assert(ret == 0); + i_stream_unref(&input); + + input = test_istream_create(test_msg); + test_istream_set_allow_eof(input, FALSE); + + parser = message_parser_init(pool, input, 0, 0); + for (i = 1; i <= TEST_MSG_LEN*2+1; i++) { + test_istream_set_size(input, i/2); + if (i > TEST_MSG_LEN*2) + test_istream_set_allow_eof(input, TRUE); + while ((ret = message_parser_parse_next_block(parser, + &block)) > 0) ; + if (ret < 0 && i < TEST_MSG_LEN*2) { + success = FALSE; + break; + } + } + ret = message_parser_deinit(&parser, &parts2); + i_assert(ret == 0); + i_stream_unref(&input); + + if (!msg_parts_cmp(parts, parts2)) + success = FALSE; + + pool_unref(&pool); + test_out("message_parser()", success); +} + +static void test_rfc2231_parser(void) +{ + const char *input = + "; key*2=ba%" + "; key2*0=a" + "; key3*0*=us-ascii'en'xyz" + "; key*0=\"foo\"" + "; key2*1*=b%25" + "; key3*1=plop%" + "; key*1=baz"; + const char *output[] = { + "key", + "foobazba%", + "key2*", + "''ab%25", + "key3*", + "us-ascii'en'xyzplop%25", + NULL + }; + struct rfc822_parser_context parser; + const char *const *result; + unsigned int i; + bool success; + + rfc822_parser_init(&parser, (const void *)input, strlen(input), NULL); + if (rfc2231_parse(&parser, &result) < 0) + success = FALSE; + else { + success = TRUE; + for (i = 0; output[i] != NULL && result[i] != NULL; i++) { + if (strcmp(output[i], result[i]) != 0) + break; + } + if (output[i] != NULL || result[i] != NULL) + success = FALSE; + } + test_out("rfc2231_parse()", success); +} + +static void filter_callback(struct message_header_line *hdr, + bool *matched, void *context ATTR_UNUSED) +{ + if (hdr != NULL && hdr->name_offset == 0) { + /* drop first header */ + *matched = TRUE; + } +} + +static void test_istream_filter(void) +{ + static const char *exclude_headers[] = { "To", NULL }; + const char *input = "From: foo\nFrom: abc\nTo: bar\n\nhello world\n"; + const char *output = "From: abc\n\nhello world\n"; + struct istream *istream, *filter; + unsigned int i, input_len = strlen(input); + unsigned int output_len = strlen(output); + const unsigned char *data; + size_t size; + ssize_t ret; + bool success = TRUE; + + istream = test_istream_create(input); + filter = i_stream_create_header_filter(istream, + HEADER_FILTER_EXCLUDE | + HEADER_FILTER_NO_CR, + exclude_headers, 1, + filter_callback, NULL); + for (i = 1; i <= input_len; i++) { + test_istream_set_size(istream, i); + ret = i_stream_read(filter); + if (ret < 0) { + success = FALSE; + break; + } + } + data = i_stream_get_data(filter, &size); + if (size != output_len || memcmp(data, output, size) != 0) + success = FALSE; + + i_stream_skip(filter, size); + i_stream_seek(filter, 0); + while ((ret = i_stream_read(filter)) > 0) ; + data = i_stream_get_data(filter, &size); + if (size != output_len || memcmp(data, output, size) != 0) + success = FALSE; + + i_stream_unref(&filter); + i_stream_unref(&istream); + + test_out("i_stream_create_header_filter()", success); +} + +int main(void) +{ + static void (*test_functions[])(void) = { + test_message_address, + test_message_date_parse, + test_message_parser, + test_rfc2231_parser, + test_istream_filter, + + NULL + }; + return test_run(test_functions); +} diff -r 2090983d37fa -r 665ea7a8d26e src/lib-test/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-test/Makefile.am Sat May 09 16:20:41 2009 -0400 @@ -0,0 +1,18 @@ +noinst_LTLIBRARIES = libtest.la + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-charset + +libtest_la_SOURCES = \ + test-common.c + +headers = \ + test-common.h + +if INSTALL_HEADERS + pkginc_libdir=$(pkgincludedir) + pkginc_lib_HEADERS = $(headers) +else + noinst_HEADERS = $(headers) +endif diff -r 2090983d37fa -r 665ea7a8d26e src/lib-test/test-common.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-test/test-common.c Sat May 09 16:20:41 2009 -0400 @@ -0,0 +1,141 @@ +/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "istream-internal.h" +#include "test-common.h" + +#include + +#define OUT_NAME_ALIGN 70 + +static char *test_prefix; +static bool test_success; +static unsigned int failure_count; +static unsigned int total_count; + +static ssize_t test_read(struct istream_private *stream) +{ + if (stream->pos < (uoff_t)stream->statbuf.st_size) + return 0; + + stream->istream.eof = TRUE; + return -1; +} + +static ssize_t test_noread(struct istream_private *stream ATTR_UNUSED) +{ + return 0; +} + +struct istream *test_istream_create(const char *data) +{ + struct istream *input; + unsigned int len = strlen(data); + + input = i_stream_create_from_data(data, len); + input->blocking = FALSE; + input->real_stream->statbuf.st_size = len; + input->real_stream->read = test_read; + return input; +} + +void test_istream_set_size(struct istream *input, uoff_t size) +{ + input->real_stream->pos = size; +} + +void test_istream_set_allow_eof(struct istream *input, bool allow) +{ + input->real_stream->read = allow ? test_read : test_noread; +} + +void test_begin(const char *name) +{ + i_assert(test_prefix == NULL); + test_prefix = i_strdup(name); + test_success = TRUE; +} + +void test_assert_failed(const char *code, const char *file, unsigned int line) +{ + printf("%s:%u: Assert failed: %s\n", file, line, code); + test_success = FALSE; +} + +void test_end(void) +{ + i_assert(test_prefix != NULL); + + test_out("", test_success); + i_free_and_null(test_prefix); + test_success = FALSE; +} + +void test_out(const char *name, bool success) +{ + test_out_reason(name, success, NULL); +} + +void test_out_reason(const char *name, bool success, const char *reason) +{ + int i = 0; + + if (test_prefix != NULL) { + fputs(test_prefix, stdout); + i += strlen(test_prefix); + if (*name != '\0') { + putchar(':'); + i++; + } + putchar(' '); + i++; + } + if (*name != '\0') { + fputs(name, stdout); + putchar(' '); + i += strlen(name) + 1; + } + for (; i < OUT_NAME_ALIGN; i++) + putchar('.'); + fputs(" : ", stdout); + if (success) + fputs("ok", stdout); + else { + fputs("FAILED", stdout); + test_success = FALSE; + failure_count++; + } + if (reason != NULL && *reason != '\0') + printf(": %s", reason); + putchar('\n'); + total_count++; +} + +static void test_init(void) +{ + test_prefix = NULL; + failure_count = 0; + total_count = 0; + + lib_init(); +} + +static int test_deinit(void) +{ + i_assert(test_prefix == NULL); + printf("%u / %u tests failed\n", failure_count, total_count); + return failure_count == 0 ? 0 : 1; +} + +int test_run(void (*test_functions[])(void)) +{ + unsigned int i; + + test_init(); + for (i = 0; test_functions[i] != NULL; i++) { + T_BEGIN { + test_functions[i](); + } T_END; + } + return test_deinit(); +} diff -r 2090983d37fa -r 665ea7a8d26e src/lib-test/test-common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-test/test-common.h Sat May 09 16:20:41 2009 -0400 @@ -0,0 +1,20 @@ +#ifndef TEST_COMMON_H +#define TEST_COMMON_H + +struct istream *test_istream_create(const char *data); +void test_istream_set_size(struct istream *input, uoff_t size); +void test_istream_set_allow_eof(struct istream *input, bool allow); + +void test_begin(const char *name); +#define test_assert(code) STMT_START { \ + if (!(code)) test_assert_failed(#code, __FILE__, __LINE__); \ + } STMT_END +void test_assert_failed(const char *code, const char *file, unsigned int line); +void test_end(void); + +void test_out(const char *name, bool success); +void test_out_reason(const char *name, bool success, const char *reason); + +int test_run(void (*test_functions[])(void)); + +#endif diff -r 2090983d37fa -r 665ea7a8d26e src/lib-test/test-common.lo --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-test/test-common.lo Sat May 09 16:20:41 2009 -0400 @@ -0,0 +1,12 @@ +# test-common.lo - a libtool object file +# Generated by ltmain.sh (GNU libtool) 2.2.6 Debian-2.2.6a-1ubuntu1 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/test-common.o' + +# Name of the non-PIC object +non_pic_object='test-common.o' + diff -r 2090983d37fa -r 665ea7a8d26e src/lib-test/test-common.o Binary file src/lib-test/test-common.o has changed diff -r 2090983d37fa -r 665ea7a8d26e src/lib/Makefile.am --- a/src/lib/Makefile.am Sat May 09 16:10:36 2009 -0400 +++ b/src/lib/Makefile.am Sat May 09 16:20:41 2009 -0400 @@ -198,9 +198,36 @@ var-expand.h \ write-full.h +test_programs = test-lib +noinst_PROGRAMS = $(test_programs) + +test_lib_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-test + +test_libs = \ + ../lib-test/libtest.la \ + liblib.la + +test_lib_SOURCES = \ + test-lib.c \ + test-istream.c + +test_headers = \ + test-lib.h + +test_lib_LDADD = $(test_libs) +test_lib_DEPENDENCIES = $(test_libs) + +check: check-am check-test +check-test: $(test_programs) + for bin in $(test_programs); do \ + if ! ./$$bin; then exit 1; fi \ + done + if INSTALL_HEADERS pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) + noinst_HEADERS = $(test_headers) else - noinst_HEADERS = $(headers) + noinst_HEADERS = $(headers) $(test_headers) endif diff -r 2090983d37fa -r 665ea7a8d26e src/lib/test-istream.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/test-istream.c Sat May 09 16:20:41 2009 -0400 @@ -0,0 +1,105 @@ +/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "str.h" +#include "istream-internal.h" +#include "istream-crlf.h" + +static void test_istream_crlf_input(const char *input, unsigned int num) +{ + string_t *output; + const unsigned char *data; + size_t size; + ssize_t ret; + unsigned int i, j, pos, input_len = strlen(input); + struct istream *istream, *crlf_istream; + bool success; + + output = t_str_new(256); + + for (j = 0; j < 4; j++) { + istream = i_stream_create_from_data(input, input_len); + success = TRUE; + 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 || 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; + 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); + } + ret = i_stream_read(crlf_istream); + data = i_stream_get_data(crlf_istream, &size); + if (ret > 0) { + if (pos + (unsigned int)ret != size) { + success = FALSE; + break; + } + pos += ret; + } + if (memcmp(data, str_data(output), size) != 0) { + success = FALSE; + break; + } + } + if (size != str_len(output)) + success = FALSE; + i_stream_unref(&crlf_istream); + i_stream_unref(&istream); + + test_out(t_strdup_printf("test_istream_crlf(%d)", num*4+j), + success); + } +} + +static 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; + + for (i = 0; i < N_ELEMENTS(input); i++) + test_istream_crlf_input(input[i], i); +} + +void test_istreams(void) +{ + test_istream_crlf(); +} diff -r 2090983d37fa -r 665ea7a8d26e src/lib/test-lib.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/test-lib.c Sat May 09 16:20:41 2009 -0400 @@ -0,0 +1,923 @@ +/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "array.h" +#include "str.h" +#include "base64.h" +#include "bsearch-insert-pos.h" +#include "aqueue.h" +#include "network.h" +#include "primes.h" +#include "priorityq.h" +#include "seq-range-array.h" +#include "str-find.h" +#include "str-sanitize.h" +#include "utc-mktime.h" + +#include +#include + +static void test_array(void) +{ + ARRAY_DEFINE(intarr, int); + int input[] = { -1234567890, -272585721, 2724859223U, 824725652 }; + const int *output; + unsigned int i, j; + bool success = TRUE; + + t_array_init(&intarr, 5); + for (i = 0; i < N_ELEMENTS(input); i++) { + array_clear(&intarr); + array_append(&intarr, input, i); + array_reverse(&intarr); + + output = i == 0 ? NULL : array_idx(&intarr, 0); + for (j = 0; j < i; j++) { + if (input[i-j-1] != output[j]) { + success = FALSE; + break; + } + } + } + test_out("array_reverse()", success); +} + +static void test_base64_encode(void) +{ + static const char *input[] = { + "hello world", + "foo barits", + "just niin" + }; + static const char *output[] = { + "aGVsbG8gd29ybGQ=", + "Zm9vIGJhcml0cw==", + "anVzdCBuaWlu" + }; + string_t *str, *dest; + unsigned int i, j, max; + char buf[10]; + bool success; + + str = t_str_new(256); + dest = t_str_new(256); + for (i = 0; i < N_ELEMENTS(input); i++) { + str_truncate(str, 0); + base64_encode(input[i], strlen(input[i]), str); + success = strcmp(output[i], str_c(str)) == 0; + test_out(t_strdup_printf("base64_encode(%d)", i), success); + } + + for (i = 0; i < 1000; i++) { + max = rand() % sizeof(buf); + for (j = 0; j < max; j++) + buf[j] = rand(); + + str_truncate(str, 0); + str_truncate(dest, 0); + base64_encode(buf, max, str); + base64_decode(str_data(str), str_len(str), NULL, dest); + if (str_len(dest) != max && + memcmp(buf, str_data(dest), max) != 0) + break; + } + test_out("base64 random", success); +} + +struct test_base64_decode_output { + const char *text; + int ret; + unsigned int src_pos; +}; + +static void test_base64_decode(void) +{ + static const char *input[] = { + "\taGVsbG8gd29ybGQ=", + "\nZm9v\n \tIGJh \t\ncml0cw==", + " anVzdCBuaWlu \n", + "aGVsb", + "aGVsb!!!!!", + "aGVs!!!!!" + }; + static const struct test_base64_decode_output output[] = { + { "hello world", 0, -1 }, + { "foo barits", 0, -1 }, + { "just niin", 1, -1 }, + { "hel", 1, 4 }, + { "hel", -1, 4 }, + { "hel", -1, 4 } + }; + string_t *str; + unsigned int i; + size_t src_pos; + int ret; + bool success; + + str = t_str_new(256); + for (i = 0; i < N_ELEMENTS(input); i++) { + str_truncate(str, 0); + + src_pos = 0; + ret = base64_decode(input[i], strlen(input[i]), &src_pos, str); + + success = output[i].ret == ret && + strcmp(output[i].text, str_c(str)) == 0 && + (src_pos == output[i].src_pos || + (output[i].src_pos == (unsigned int)-1 && + src_pos == strlen(input[i]))); + test_out(t_strdup_printf("base64_decode(%d)", i), success); + } +} + +static int cmp_uint(const void *p1, const void *p2) +{ + const unsigned int *i1 = p1, *i2 = p2; + + return *i1 - *i2; +} + +static void test_bsearch_insert_pos(void) +{ + static const unsigned int input[] = { + 1, 5, 9, 15, 16, -1, + 1, 5, 9, 15, 16, 17, -1, + -1 + }; + static const unsigned int max_key = 18; + const unsigned int *cur; + unsigned int key, len, i, idx; + bool success; + + cur = input; + for (i = 0; cur[0] != -1U; i++) { + for (len = 0; cur[len] != -1U; len++) ; + for (key = 0; key < max_key; key++) { + if (bsearch_insert_pos(&key, cur, len, sizeof(*cur), + cmp_uint, &idx)) + success = cur[idx] == key; + else if (idx == 0) + success = cur[0] > key; + else if (idx == len) + success = cur[len-1] < key; + else { + success = cur[idx-1] < key && + cur[idx+1] > key; + } + if (!success) + break; + } + cur += len + 1; + + test_out(t_strdup_printf("bsearch_insert_pos(%d,%d)", i, key), + success); + } +} + +static void test_buffer(void) +{ +#define BUF_TEST_SIZE (1024*2) +#define BUF_TEST_COUNT 1000 + buffer_t *buf; + unsigned char *p, testdata[BUF_TEST_SIZE], shadowbuf[BUF_TEST_SIZE]; + unsigned int i, shadowbuf_size; + size_t pos, pos2, size; + int test = -1; + bool zero; + + buf = buffer_create_dynamic(default_pool, 1); + for (i = 0; i < BUF_TEST_SIZE; i++) + testdata[i] = random(); + memset(shadowbuf, 0, sizeof(shadowbuf)); + + srand(1); + shadowbuf_size = 0; + for (i = 0; i < BUF_TEST_COUNT; i++) { + if (buf->used == BUF_TEST_SIZE) { + size = shadowbuf_size = rand() % (buf->used - 1); + buffer_set_used_size(buf, size); + memset(shadowbuf + shadowbuf_size, 0, + BUF_TEST_SIZE - shadowbuf_size); + i_assert(buf->used < BUF_TEST_SIZE); + } + + test = rand() % 6; + zero = rand() % 10 == 0; + switch (test) { + case 0: + pos = rand() % (BUF_TEST_SIZE-1); + size = rand() % (BUF_TEST_SIZE - pos); + if (!zero) { + buffer_write(buf, pos, testdata, size); + memcpy(shadowbuf + pos, testdata, size); + } else { + buffer_write_zero(buf, pos, size); + memset(shadowbuf + pos, 0, size); + } + if (pos + size > shadowbuf_size) + shadowbuf_size = pos + size; + break; + case 1: + size = rand() % (BUF_TEST_SIZE - buf->used); + if (!zero) { + buffer_append(buf, testdata, size); + memcpy(shadowbuf + shadowbuf_size, + testdata, size); + } else { + buffer_append_zero(buf, size); + memset(shadowbuf + shadowbuf_size, 0, size); + } + shadowbuf_size += size; + break; + case 2: + pos = rand() % (BUF_TEST_SIZE-1); + size = rand() % (BUF_TEST_SIZE - I_MAX(buf->used, pos)); + if (!zero) { + buffer_insert(buf, pos, testdata, size); + memmove(shadowbuf + pos + size, + shadowbuf + pos, + BUF_TEST_SIZE - (pos + size)); + memcpy(shadowbuf + pos, testdata, size); + } else { + buffer_insert_zero(buf, pos, size); + memmove(shadowbuf + pos + size, + shadowbuf + pos, + BUF_TEST_SIZE - (pos + size)); + memset(shadowbuf + pos, 0, size); + } + if (pos < shadowbuf_size) + shadowbuf_size += size; + else + shadowbuf_size = pos + size; + break; + case 3: + pos = rand() % (BUF_TEST_SIZE-1); + size = rand() % (BUF_TEST_SIZE - pos); + buffer_delete(buf, pos, size); + if (pos < shadowbuf_size) { + if (pos + size > shadowbuf_size) + size = shadowbuf_size - pos; + memmove(shadowbuf + pos, + shadowbuf + pos + size, + BUF_TEST_SIZE - (pos + size)); + + shadowbuf_size -= size; + memset(shadowbuf + shadowbuf_size, 0, + BUF_TEST_SIZE - shadowbuf_size); + } + break; + case 4: + if (shadowbuf_size == 0) + break; + pos = rand() % (shadowbuf_size-1); /* dest */ + pos2 = rand() % (shadowbuf_size-1); /* source */ + size = rand() % (shadowbuf_size - I_MAX(pos, pos2)); + buffer_copy(buf, pos, buf, pos2, size); + memmove(shadowbuf + pos, + shadowbuf + pos2, size); + if (pos > pos2 && pos + size > shadowbuf_size) + shadowbuf_size = pos + size; + break; + case 5: + pos = rand() % (BUF_TEST_SIZE-1); + size = rand() % (BUF_TEST_SIZE - pos); + p = buffer_get_space_unsafe(buf, pos, size); + memcpy(p, testdata, size); + memcpy(shadowbuf + pos, testdata, size); + if (pos + size > shadowbuf_size) + shadowbuf_size = pos + size; + break; + } + i_assert(shadowbuf_size <= BUF_TEST_SIZE); + + if (buf->used != shadowbuf_size || + memcmp(buf->data, shadowbuf, buf->used) != 0) + break; + } + if (i == BUF_TEST_COUNT) + test_out("buffer", TRUE); + else { + test_out_reason("buffer", FALSE, + t_strdup_printf("round %u test %d failed", i, test)); + } + buffer_free(&buf); +} + +static bool aqueue_is_ok(struct aqueue *aqueue, unsigned int deleted_n) +{ + const unsigned int *p; + unsigned int n, i, count; + + count = aqueue_count(aqueue); + for (i = 0, n = 1; i < count; i++, n++) { + p = array_idx_i(aqueue->arr, aqueue_idx(aqueue, i)); + if (i == deleted_n) + n++; + if (*p != n) + return FALSE; + } + return TRUE; +} + +static const unsigned int aqueue_input[] = { 1, 2, 3, 4, 5, 6 }; +static const char *test_aqueue2(unsigned int initial_size) +{ + ARRAY_DEFINE(aqueue_array, unsigned int); + unsigned int i, j, k; + struct aqueue *aqueue; + + for (i = 0; i < N_ELEMENTS(aqueue_input); i++) { + for (k = 0; k < N_ELEMENTS(aqueue_input); k++) { + t_array_init(&aqueue_array, initial_size); + aqueue = aqueue_init(&aqueue_array.arr); + aqueue->head = aqueue->tail = initial_size - 1; + for (j = 0; j < k; j++) { + aqueue_append(aqueue, &aqueue_input[j]); + if (aqueue_count(aqueue) != j + 1) { + return t_strdup_printf("Wrong count after append %u vs %u)", + aqueue_count(aqueue), j + 1); + } + if (!aqueue_is_ok(aqueue, -1U)) + return "Invalid data after append"; + } + + if (k != 0 && i < k) { + aqueue_delete(aqueue, i); + if (aqueue_count(aqueue) != k - 1) + return "Wrong count after delete"; + if (!aqueue_is_ok(aqueue, i)) + return "Invalid data after delete"; + } + } + } + aqueue_clear(aqueue); + if (aqueue_count(aqueue) != 0) + return "aqueue_clear() broken"; + return NULL; +} + +static void test_aqueue(void) +{ + unsigned int i; + const char *reason = NULL; + + for (i = 1; i <= N_ELEMENTS(aqueue_input) + 1 && reason == NULL; i++) { + T_BEGIN { + reason = test_aqueue2(i); + } T_END; + } + test_out_reason("aqueue", reason == NULL, reason); +} + +static bool mem_has_bytes(const void *mem, size_t size, uint8_t b) +{ + const uint8_t *bytes = mem; + unsigned int i; + + for (i = 0; i < size; i++) { + if (bytes[i] != b) + return FALSE; + } + return TRUE; +} + +static void test_mempool_alloconly(void) +{ +#define PMALLOC_MAX_COUNT 128 + pool_t pool; + unsigned int i, j, k; + void *mem[PMALLOC_MAX_COUNT + 1]; + bool success = TRUE; + + for (i = 0; i < 64; i++) { + for (j = 1; j <= 128; j++) { + pool = pool_alloconly_create(MEMPOOL_GROWING"test", i); + mem[0] = p_malloc(pool, j); + memset(mem[0], j, j); + + for (k = 1; k <= PMALLOC_MAX_COUNT; k++) { + mem[k] = p_malloc(pool, k); + memset(mem[k], k, k); + } + + if (!mem_has_bytes(mem[0], j, j)) + success = FALSE; + for (k = 1; k <= PMALLOC_MAX_COUNT; k++) { + if (!mem_has_bytes(mem[k], k, k)) + success = FALSE; + } + pool_unref(&pool); + } + } + test_out("mempool_alloconly", success); +} + +struct test_net_is_in_network_input { + const char *ip; + const char *net; + unsigned int bits; + bool ret; +}; + +static void test_net_is_in_network(void) +{ + static struct test_net_is_in_network_input input[] = { + { "1.2.3.4", "1.2.3.4", 32, TRUE }, + { "1.2.3.4", "1.2.3.3", 32, FALSE }, + { "1.2.3.4", "1.2.3.5", 32, FALSE }, + { "1.2.3.4", "1.2.2.4", 32, FALSE }, + { "1.2.3.4", "1.1.3.4", 32, FALSE }, + { "1.2.3.4", "0.2.3.4", 32, FALSE }, + { "1.2.3.253", "1.2.3.254", 31, FALSE }, + { "1.2.3.254", "1.2.3.254", 31, TRUE }, + { "1.2.3.255", "1.2.3.254", 31, TRUE }, + { "1.2.3.255", "1.2.3.0", 24, TRUE }, + { "1.2.255.255", "1.2.254.0", 23, TRUE }, + { "255.255.255.255", "128.0.0.0", 1, TRUE }, + { "255.255.255.255", "127.0.0.0", 1, FALSE } +#ifdef HAVE_IPV6 + , + { "1234:5678::abcf", "1234:5678::abce", 127, TRUE }, + { "1234:5678::abcd", "1234:5678::abce", 127, FALSE }, + { "123e::ffff", "123e::0", 15, TRUE }, + { "123d::ffff", "123e::0", 15, FALSE } +#endif + }; + struct ip_addr ip, net_ip; + unsigned int i; + bool success; + + for (i = 0; i < N_ELEMENTS(input); i++) { + net_addr2ip(input[i].ip, &ip); + net_addr2ip(input[i].net, &net_ip); + success = net_is_in_network(&ip, &net_ip, input[i].bits) == + input[i].ret; + test_out(t_strdup_printf("net_is_in_network(%u)", i), success); + } +} + +struct pq_test_item { + struct priorityq_item item; + int num; +}; + +static int cmp_int(const void *p1, const void *p2) +{ + const struct pq_test_item *i1 = p1, *i2 = p2; + + return i1->num - i2->num; +} + +static void test_primes(void) +{ + unsigned int i, j, num; + bool success; + + success = primes_closest(0) > 0; + for (num = 1; num < 1024; num++) { + if (primes_closest(num) < num) + success = FALSE; + } + for (i = 10; i < 32; i++) { + num = (1 << i) - 100; + for (j = 0; j < 200; j++, num++) { + if (primes_closest(num) < num) + success = FALSE; + } + } + test_out("primes_closest()", success); +} + +static void test_priorityq(void) +{ +#define PQ_MAX_ITEMS 100 + static const int input[] = { + 1, 2, 3, 4, 5, 6, 7, 8, -1, + 8, 7, 6, 5, 4, 3, 2, 1, -1, + 8, 7, 5, 6, 1, 3, 4, 2, -1, + -1 + }; + static const int output[] = { + 1, 2, 3, 4, 5, 6, 7, 8 + }; + struct pq_test_item *item, items[PQ_MAX_ITEMS]; + unsigned int i, j; + struct priorityq *pq; + pool_t pool; + int prev; + bool success = TRUE; + + pool = pool_alloconly_create("priorityq items", 1024); + + /* simple tests with popping only */ + for (i = 0; input[i] != -1; i++) { + p_clear(pool); + pq = priorityq_init(cmp_int, 1); + for (j = 0; input[i] != -1; i++, j++) { + if (priorityq_count(pq) != j) + success = FALSE; + item = p_new(pool, struct pq_test_item, 1); + item->num = input[i]; + priorityq_add(pq, &item->item); + } + for (j = 0; j < N_ELEMENTS(output); j++) { + if (priorityq_count(pq) != N_ELEMENTS(output) - j) + success = FALSE; + + item = (struct pq_test_item *)priorityq_peek(pq); + if (output[j] != item->num) + success = FALSE; + item = (struct pq_test_item *)priorityq_pop(pq); + if (output[j] != item->num) + success = FALSE; + } + if (priorityq_count(pq) != 0) + success = FALSE; + if (priorityq_peek(pq) != NULL || priorityq_pop(pq) != NULL) + success = FALSE; + priorityq_deinit(&pq); + } + test_out("priorityq(1)", success); + + /* randomized tests, remove elements */ + success = TRUE; + for (i = 0; i < 100; i++) { + pq = priorityq_init(cmp_int, 1); + for (j = 0; j < PQ_MAX_ITEMS; j++) { + items[j].num = rand(); + priorityq_add(pq, &items[j].item); + } + for (j = 0; j < PQ_MAX_ITEMS; j++) { + if (rand() % 3 == 0) { + priorityq_remove(pq, &items[j].item); + items[j].num = -1; + } + } + prev = 0; + while (priorityq_count(pq) > 0) { + item = (struct pq_test_item *)priorityq_pop(pq); + if (item->num < 0 || prev > item->num) + success = FALSE; + prev = item->num; + item->num = -1; + } + for (j = 0; j < PQ_MAX_ITEMS; j++) { + if (items[j].num != -1) + success = FALSE; + } + priorityq_deinit(&pq); + } + test_out("priorityq(2)", success); + pool_unref(&pool); +} + +static void test_seq_range_array_random(void) +{ +#define SEQ_RANGE_TEST_BUFSIZE 20 +#define SEQ_RANGE_TEST_COUNT 10000 + unsigned char shadowbuf[SEQ_RANGE_TEST_BUFSIZE]; + ARRAY_TYPE(seq_range) range; + const struct seq_range *seqs; + uint32_t seq1, seq2; + unsigned int i, j, ret, ret2, count; + int test = -1; + + ret = ret2 = 0; + i_array_init(&range, 1); + memset(shadowbuf, 0, sizeof(shadowbuf)); + for (i = 0; i < SEQ_RANGE_TEST_COUNT; i++) { + seq1 = rand() % SEQ_RANGE_TEST_BUFSIZE; + seq2 = seq1 + rand() % (SEQ_RANGE_TEST_BUFSIZE - seq1); + test = rand() % 4; + switch (test) { + case 0: + seq_range_array_add(&range, 0, seq1); + shadowbuf[seq1] = 1; + break; + case 1: + seq_range_array_add_range(&range, seq1, seq2); + memset(shadowbuf + seq1, 1, seq2 - seq1 + 1); + break; + case 2: + ret = seq_range_array_remove(&range, seq1) ? 1 : 0; + ret2 = shadowbuf[seq1] != 0 ? 1 : 0; + shadowbuf[seq1] = 0; + break; + case 3: + ret = seq_range_array_remove_range(&range, seq1, seq2); + for (ret2 = 0; seq1 <= seq2; seq1++) { + if (shadowbuf[seq1] != 0) { + ret2++; + shadowbuf[seq1] = 0; + } + } + break; + } + if (ret != ret2) + break; + + seqs = array_get(&range, &count); + for (j = 0, seq1 = 0; j < count; j++) { + if (j > 0 && seqs[j-1].seq2 >= seqs[j].seq1) + goto fail; + for (; seq1 < seqs[j].seq1; seq1++) { + if (shadowbuf[seq1] != 0) + goto fail; + } + for (; seq1 <= seqs[j].seq2; seq1++) { + if (shadowbuf[seq1] == 0) + goto fail; + } + } + i_assert(seq1 <= SEQ_RANGE_TEST_BUFSIZE); + for (; seq1 < SEQ_RANGE_TEST_BUFSIZE; seq1++) { + if (shadowbuf[seq1] != 0) + goto fail; + } + } +fail: + if (i == SEQ_RANGE_TEST_COUNT) + test_out("seq_range_array random", TRUE); + else { + test_out_reason("seq_range_array random", FALSE, + t_strdup_printf("round %u test %d failed", i, test)); + } +} + +static void test_seq_range_array_invert(void) +{ + static const unsigned int input_min = 1, input_max = 5; + static const unsigned int input[] = { + 1, 2, 3, 4, 5, -1U, + 2, 3, 4, -1U, + 1, 2, 4, 5, -1U, + 1, 3, 5, -1U, + 1, -1U, + 5, -1U, + -1U + }; + ARRAY_TYPE(seq_range) range = ARRAY_INIT; + unsigned int i, j, seq, start, num; + bool old_exists, success; + + for (i = num = 0; input[i] != -1U; num++, i++) { + success = TRUE; + start = i; + for (; input[i] != -1U; i++) { + seq_range_array_add(&range, 32, input[i]); + for (j = start; j < i; j++) { + if (!seq_range_exists(&range, input[j])) + success = FALSE; + } + } + + seq_range_array_invert(&range, input_min, input_max); + for (seq = input_min; seq <= input_max; seq++) { + for (j = start; input[j] != -1U; j++) { + if (input[j] == seq) + break; + } + old_exists = input[j] != -1U; + if (seq_range_exists(&range, seq) == old_exists) + success = FALSE; + } + test_out(t_strdup_printf("seq_range_array_invert(%u)", num), + success); + array_free(&range); + } +} + +static void test_seq_range_create(ARRAY_TYPE(seq_range) *array, uint8_t byte) +{ + unsigned int i; + + array_clear(array); + for (i = 0; i < 8; i++) { + if ((byte & (1 << i)) != 0) + seq_range_array_add(array, 0, i + 1); + } +} + +static void test_seq_range_array_have_common(void) +{ + ARRAY_TYPE(seq_range) arr1, arr2; + unsigned int i, j; + bool ret1, ret2, success = TRUE; + + t_array_init(&arr1, 8); + t_array_init(&arr2, 8); + for (i = 0; i < 256; i++) { + test_seq_range_create(&arr1, i); + for (j = 0; j < 256; j++) { + test_seq_range_create(&arr2, j); + ret1 = seq_range_array_have_common(&arr1, &arr2); + ret2 = (i & j) != 0; + if (ret1 != ret2) + success = FALSE; + } + } + test_out("seq_range_array_have_common()", success); +} + +static void test_seq_range_array(void) +{ + test_seq_range_array_invert(); + test_seq_range_array_have_common(); + test_seq_range_array_random(); +} + +static const char *str_find_text = "xababcd"; + +static bool test_str_find_substring(const char *key, int expected_pos) +{ + const unsigned char *text = (const unsigned char *)str_find_text; + const unsigned int text_len = strlen(str_find_text); + struct str_find_context *ctx; + unsigned int i, j, pos, max, offset; + bool ret; + + ctx = str_find_init(pool_datastack_create(), key); + /* divide text into every possible block combination and test that + it matches */ + max = 1 << (text_len-1); + for (i = 0; i < max; i++) { + str_find_reset(ctx); + pos = 0; offset = 0; ret = FALSE; + for (j = 0; j < text_len; j++) { + if ((i & (1 << j)) != 0) { + if (str_find_more(ctx, text+pos, j-pos+1)) { + ret = TRUE; + break; + } + offset += j-pos + 1; + pos = j + 1; + } + } + if (pos != text_len && !ret) { + if (str_find_more(ctx, text+pos, j-pos)) + ret = TRUE; + } + if (expected_pos < 0) { + if (ret) + return FALSE; + } else { + if (!ret) + return FALSE; + + pos = str_find_get_match_end_pos(ctx) + + offset - strlen(key); + if ((int)pos != expected_pos) + return FALSE; + } + } + return TRUE; +} + +struct str_find_input { + const char *str; + int pos; +}; +static void test_str_find(void) +{ + static const char *fail_input[] = { + "xabc", + "xabd", + "abd" + }; + unsigned int idx, len; + const char *key, *p; + unsigned int i; + bool success = TRUE; + + for (idx = 0; idx < strlen(str_find_text); idx++) { + for (len = strlen(str_find_text)-idx; len > 0; len--) { + /* we'll get a search key for all substrings of text */ + T_BEGIN { + key = t_strndup(str_find_text + idx, len); + p = strstr(str_find_text, key); + success = test_str_find_substring(key, p - str_find_text); + } T_END; + if (!success) + break; + } + } + for (i = 0; i < N_ELEMENTS(fail_input) && success; i++) + success = test_str_find_substring(fail_input[i], -1); + test_out("str_find()", success); +} + +struct str_sanitize_input { + const char *str; + unsigned int max_len; +}; +static void test_str_sanitize(void) +{ + static struct str_sanitize_input input[] = { + { NULL, 2 }, + { "", 2 }, + { "a", 2 }, + { "ab", 2 }, + { "abc", 2 }, + { "abcd", 3 }, + { "abcde", 4 } + }; + static const char *output[] = { + NULL, + "", + "a", + "ab", + "...", + "...", + "a..." + }; + const char *str; + unsigned int i; + bool success; + + for (i = 0; i < N_ELEMENTS(input); i++) { + str = str_sanitize(input[i].str, input[i].max_len); + success = null_strcmp(output[i], str) == 0; + test_out(t_strdup_printf("str_sanitize(%d)", i), success); + } +} + +struct test_message_date_output { + time_t time; + int tz_offset; + bool ret; +}; + +struct test_utc_mktime_input { + int year, month, day, hour, min, sec; +}; + +static void test_utc_mktime(void) +{ + static struct test_utc_mktime_input input[] = { +#ifdef TIME_T_SIGNED + { 1969, 12, 31, 23, 59, 59 }, + { 1901, 12, 13, 20, 45, 53 }, +#endif +#if (TIME_T_MAX_BITS > 32 || !defined(TIME_T_SIGNED)) + { 2106, 2, 7, 6, 28, 15 }, +#endif + { 2007, 11, 7, 1, 7, 20 }, + { 1970, 1, 1, 0, 0, 0 }, + { 2038, 1, 19, 3, 14, 7 } + }; + static time_t output[] = { +#ifdef TIME_T_SIGNED + -1, + -2147483647, +#endif +#if (TIME_T_MAX_BITS > 32 || !defined(TIME_T_SIGNED)) + 4294967295, +#endif + 1194397640, + 0, + 2147483647 + }; + struct tm tm; + unsigned int i; + time_t t; + bool success; + + for (i = 0; i < N_ELEMENTS(input); i++) { + memset(&tm, 0, sizeof(tm)); + tm.tm_year = input[i].year - 1900; + tm.tm_mon = input[i].month - 1; + tm.tm_mday = input[i].day; + tm.tm_hour = input[i].hour; + tm.tm_min = input[i].min; + tm.tm_sec = input[i].sec; + + t = utc_mktime(&tm); + success = t == output[i]; + test_out_reason(t_strdup_printf("utc_mktime(%d)", i), success, + success ? NULL : t_strdup_printf("%ld != %ld", + (long)t, (long)output[i])); + } +} + +int main(void) +{ + static void (*test_functions[])(void) = { + test_array, + test_aqueue, + test_base64_encode, + test_base64_decode, + test_bsearch_insert_pos, + test_buffer, + test_mempool_alloconly, + test_net_is_in_network, + test_primes, + test_priorityq, + test_seq_range_array, + test_str_find, + test_str_sanitize, + test_utc_mktime, + + test_istreams + }; + return test_run(test_functions); +} diff -r 2090983d37fa -r 665ea7a8d26e src/lib/test-lib.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/test-lib.h Sat May 09 16:20:41 2009 -0400 @@ -0,0 +1,4 @@ +#include "lib.h" +#include "test-common.h" + +void test_istreams(void); diff -r 2090983d37fa -r 665ea7a8d26e src/tests/Makefile.am --- a/src/tests/Makefile.am Sat May 09 16:10:36 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -noinst_PROGRAMS = test-lib test-mail test-imap - -noinst_LIBRARIES = libtest.a - -AM_CPPFLAGS = \ - -I$(top_srcdir)/src/lib \ - -I$(top_srcdir)/src/lib-mail \ - -I$(top_srcdir)/src/lib-imap \ - -I$(top_srcdir)/src/lib-index \ - -I$(top_srcdir)/src/lib-storage - -libtest_a_SOURCES = \ - test-common.c - -libs = \ - libtest.a \ - $(LIBDOVECOT) - -test_lib_SOURCES = \ - test-istream.c \ - test-lib.c - -test_lib_LDADD = $(libs) -test_lib_DEPENDENCIES = $(libs) - -test_mail_SOURCES = \ - test-mail.c - -test_mail_LDADD = $(libs) -test_mail_DEPENDENCIES = $(libs) - -test_imap_SOURCES = \ - test-imap.c - -noinst_HEADERS = \ - test-common.h \ - test-lib.h - -test_imap_LDADD = $(libs) -test_imap_DEPENDENCIES = $(libs) diff -r 2090983d37fa -r 665ea7a8d26e src/tests/test-common.c --- a/src/tests/test-common.c Sat May 09 16:10:36 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "istream-internal.h" -#include "test-common.h" - -#include - -#define OUT_NAME_ALIGN 30 - -static unsigned int failure_count; -static unsigned int total_count; - -static ssize_t test_read(struct istream_private *stream) -{ - if (stream->pos < (uoff_t)stream->statbuf.st_size) - return 0; - - stream->istream.eof = TRUE; - return -1; -} - -static ssize_t test_noread(struct istream_private *stream ATTR_UNUSED) -{ - return 0; -} - -struct istream *test_istream_create(const char *data) -{ - struct istream *input; - unsigned int len = strlen(data); - - input = i_stream_create_from_data(data, len); - input->blocking = FALSE; - input->real_stream->statbuf.st_size = len; - input->real_stream->read = test_read; - return input; -} - -void test_istream_set_size(struct istream *input, uoff_t size) -{ - input->real_stream->pos = size; -} - -void test_istream_set_allow_eof(struct istream *input, bool allow) -{ - input->real_stream->read = allow ? test_read : test_noread; -} - -void test_out(const char *name, bool success) -{ - test_out_reason(name, success, NULL); -} - -void test_out_reason(const char *name, bool success, const char *reason) -{ - int i; - - fputs(name, stdout); - putchar(' '); - for (i = strlen(name) + 1; i < OUT_NAME_ALIGN; i++) - putchar('.'); - fputs(" : ", stdout); - if (success) - fputs("ok", stdout); - else { - fputs("FAILED", stdout); - failure_count++; - } - if (reason != NULL && *reason != '\0') - printf(": %s", reason); - putchar('\n'); - total_count++; -} - -void test_init(void) -{ - failure_count = 0; - total_count = 0; - - lib_init(); -} - -int test_deinit(void) -{ - printf("%u / %u tests failed\n", failure_count, total_count); - return failure_count == 0 ? 0 : 1; -} diff -r 2090983d37fa -r 665ea7a8d26e src/tests/test-common.h --- a/src/tests/test-common.h Sat May 09 16:10:36 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -#ifndef TEST_COMMON_H -#define TEST_COMMON_H - -struct istream *test_istream_create(const char *data); -void test_istream_set_size(struct istream *input, uoff_t size); -void test_istream_set_allow_eof(struct istream *input, bool allow); - -void test_out(const char *name, bool success); -void test_out_reason(const char *name, bool success, const char *reason); - -void test_init(void); -int test_deinit(void); - -#endif diff -r 2090983d37fa -r 665ea7a8d26e src/tests/test-imap.c --- a/src/tests/test-imap.c Sat May 09 16:10:36 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,176 +0,0 @@ -/* Copyright (c) 2008-2009 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "str.h" -#include "unichar.h" -#include "imap-match.h" -#include "imap-utf7.h" -#include "test-common.h" - -struct test_imap_match { - const char *pattern; - const char *input; - enum imap_match_result result; -}; - -static void test_imap_match(void) -{ - struct test_imap_match test[] = { - { "", "", IMAP_MATCH_YES }, - { "a", "b", IMAP_MATCH_NO }, - { "foo", "foo", IMAP_MATCH_YES }, - { "foo", "foo/", IMAP_MATCH_PARENT }, - { "%", "", IMAP_MATCH_YES }, - { "%", "foo", IMAP_MATCH_YES }, - { "%", "foo/", IMAP_MATCH_PARENT }, - { "%/", "foo/", IMAP_MATCH_YES }, - { "%", "foo/bar", IMAP_MATCH_PARENT }, - { "%/%", "foo", IMAP_MATCH_CHILDREN }, - { "%/%", "foo/", IMAP_MATCH_YES }, - { "foo/bar/%", "foo", IMAP_MATCH_CHILDREN }, - { "foo/bar/%", "foo/", IMAP_MATCH_CHILDREN }, - { "foo*", "foo", IMAP_MATCH_YES }, - { "foo*", "foo/", IMAP_MATCH_YES }, - { "foo*", "fobo", IMAP_MATCH_NO }, - { "*foo*", "bar/foo/", IMAP_MATCH_YES }, - { "*foo*", "fobo", IMAP_MATCH_CHILDREN }, - { "foo*bar", "foobar/baz", IMAP_MATCH_CHILDREN | IMAP_MATCH_PARENT }, - { "*foo*", "fobo", IMAP_MATCH_CHILDREN }, - { "%/%/%", "foo/", IMAP_MATCH_CHILDREN }, - { "%/%o/%", "foo/", IMAP_MATCH_CHILDREN }, - { "%/%o/%", "foo", IMAP_MATCH_CHILDREN }, - { "inbox", "inbox", IMAP_MATCH_YES }, - { "inbox", "INBOX", IMAP_MATCH_NO } - }; - struct test_imap_match inbox_test[] = { - { "inbox", "inbox", IMAP_MATCH_YES }, - { "inbox", "iNbOx", IMAP_MATCH_YES }, - { "i%X", "iNbOx", IMAP_MATCH_YES }, - { "%I%N%B%O%X%", "inbox", IMAP_MATCH_YES }, - { "i%X/foo", "iNbOx/foo", IMAP_MATCH_YES }, - { "%I%N%B%O%X%/foo", "inbox/foo", IMAP_MATCH_YES }, - { "i%X/foo", "inbx/foo", IMAP_MATCH_NO } - }; - struct imap_match_glob *glob; - unsigned int i; - enum imap_match_result result; - - /* first try tests without inboxcasing */ - for (i = 0; i < N_ELEMENTS(test); i++) { - glob = imap_match_init(default_pool, test[i].pattern, - FALSE, '/'); - result = imap_match(glob, test[i].input); - imap_match_deinit(&glob); - - test_out(t_strdup_printf("imap_match(%d)", i), - result == test[i].result); - } - - /* inboxcasing tests */ - for (i = 0; i < N_ELEMENTS(inbox_test); i++) { - glob = imap_match_init(default_pool, inbox_test[i].pattern, - TRUE, '/'); - result = imap_match(glob, inbox_test[i].input); - imap_match_deinit(&glob); - - test_out(t_strdup_printf("imap_match(inboxcase, %d)", i), - result == inbox_test[i].result); - } -} - -static void test_imap_utf7(void) -{ - static const char *to_utf7[] = { - "&&x&&", "&-&-x&-&-", - "~peter/mail/台北/日本語", "~peter/mail/&U,BTFw-/&ZeVnLIqe-", - "tietäjä", "tiet&AOQ-j&AOQ-", - "p", NULL, - NULL - }; - static const char *invalid_utf7[] = { - "&Jjo!", - "&U,BTFw-&ZeVnLIqe-", - NULL - }; - string_t *src, *dest; - const char *orig_src; - unsigned int i, j; - unichar_t chr; - bool success, all_success = TRUE; - - src = t_str_new(256); - dest = t_str_new(256); - - for (i = 0; to_utf7[i] != NULL; i += 2) { - str_truncate(dest, 0); - if (imap_utf8_to_utf7(to_utf7[i], dest) < 0) - success = to_utf7[i+1] == NULL; - else { - success = to_utf7[i+1] != NULL && - strcmp(to_utf7[i+1], str_c(dest)) == 0; - } - if (!success) { - test_out(t_strdup_printf("imap_utf8_to_utf7(%d)", i/2), - FALSE); - all_success = FALSE; - } else if (to_utf7[i+1] != NULL) { - str_truncate(dest, 0); - if (imap_utf7_to_utf8(to_utf7[i+1], dest) < 0 || - strcmp(to_utf7[i], str_c(dest)) != 0) { - test_out(t_strdup_printf("imap_utf7_to_utf8(%d)", i/2), - FALSE); - all_success = FALSE; - } - } - } - if (all_success) - test_out("imap_utf8_to_utf7()", TRUE); - - success = TRUE; - for (chr = 0xffff; chr <= 0x10010; chr++) { - for (i = 1; i <= 10; i++) { - str_truncate(src, 0); - str_truncate(dest, 0); - for (j = 0; j < i; j++) { - if (j % 3 == 0) - str_append_c(src, 'x'); - if (j % 5 == 0) - str_append_c(src, '&'); - uni_ucs4_to_utf8_c(chr, src); - } - - orig_src = t_strdup(str_c(src)); - str_truncate(src, 0); - - if (imap_utf8_to_utf7(orig_src, dest) < 0) - success = FALSE; - else if (imap_utf7_to_utf8(str_c(dest), src) < 0) - success = FALSE; - else - success = strcmp(str_c(src), orig_src) == 0; - if (!success) - goto end; - } - } -end: - test_out("imap_utf7_to_utf8(reverse)", success); - for (i = 0; invalid_utf7[i] != NULL; i++) { - str_truncate(dest, 0); - if (imap_utf7_to_utf8(invalid_utf7[i], dest) == 0) { - test_out(t_strdup_printf("imap_utf7_to_utf8(invalid.%d)", i), - FALSE); - all_success = FALSE; - } - } - if (all_success) - test_out("imap_utf7_to_utf8(invalid)", TRUE); -} - -int main(void) -{ - test_init(); - - test_imap_match(); - test_imap_utf7(); - return test_deinit(); -} diff -r 2090983d37fa -r 665ea7a8d26e src/tests/test-istream.c --- a/src/tests/test-istream.c Sat May 09 16:10:36 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ -/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */ - -#include "test-lib.h" -#include "str.h" -#include "istream-internal.h" -#include "istream-crlf.h" - -static void test_istream_crlf_input(const char *input, unsigned int num) -{ - string_t *output; - const unsigned char *data; - size_t size; - ssize_t ret; - unsigned int i, j, pos, input_len = strlen(input); - struct istream *istream, *crlf_istream; - bool success; - - output = t_str_new(256); - - for (j = 0; j < 4; j++) { - istream = i_stream_create_from_data(input, input_len); - success = TRUE; - 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 || 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; - 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); - } - ret = i_stream_read(crlf_istream); - data = i_stream_get_data(crlf_istream, &size); - if (ret > 0) { - if (pos + (unsigned int)ret != size) { - success = FALSE; - break; - } - pos += ret; - } - if (memcmp(data, str_data(output), size) != 0) { - success = FALSE; - break; - } - } - if (size != str_len(output)) - success = FALSE; - i_stream_unref(&crlf_istream); - i_stream_unref(&istream); - - test_out(t_strdup_printf("test_istream_crlf(%d)", num*4+j), - success); - } -} - -static 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; - - for (i = 0; i < N_ELEMENTS(input); i++) - test_istream_crlf_input(input[i], i); -} - -void test_istreams(void) -{ - test_istream_crlf(); -} diff -r 2090983d37fa -r 665ea7a8d26e src/tests/test-lib.c --- a/src/tests/test-lib.c Sat May 09 16:10:36 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,931 +0,0 @@ -/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */ - -#include "test-lib.h" -#include "array.h" -#include "str.h" -#include "base64.h" -#include "bsearch-insert-pos.h" -#include "aqueue.h" -#include "network.h" -#include "primes.h" -#include "priorityq.h" -#include "seq-range-array.h" -#include "str-find.h" -#include "str-sanitize.h" -#include "utc-mktime.h" - -#include -#include - -static void test_array(void) -{ - ARRAY_DEFINE(intarr, int); - int input[] = { -1234567890, -272585721, 2724859223U, 824725652 }; - const int *output; - unsigned int i, j; - bool success = TRUE; - - t_array_init(&intarr, 5); - for (i = 0; i < N_ELEMENTS(input); i++) { - array_clear(&intarr); - array_append(&intarr, input, i); - array_reverse(&intarr); - - output = i == 0 ? NULL : array_idx(&intarr, 0); - for (j = 0; j < i; j++) { - if (input[i-j-1] != output[j]) { - success = FALSE; - break; - } - } - } - test_out("array_reverse()", success); -} - -static void test_base64_encode(void) -{ - static const char *input[] = { - "hello world", - "foo barits", - "just niin" - }; - static const char *output[] = { - "aGVsbG8gd29ybGQ=", - "Zm9vIGJhcml0cw==", - "anVzdCBuaWlu" - }; - string_t *str, *dest; - unsigned int i, j, max; - char buf[10]; - bool success; - - str = t_str_new(256); - dest = t_str_new(256); - for (i = 0; i < N_ELEMENTS(input); i++) { - str_truncate(str, 0); - base64_encode(input[i], strlen(input[i]), str); - success = strcmp(output[i], str_c(str)) == 0; - test_out(t_strdup_printf("base64_encode(%d)", i), success); - } - - for (i = 0; i < 1000; i++) { - max = rand() % sizeof(buf); - for (j = 0; j < max; j++) - buf[j] = rand(); - - str_truncate(str, 0); - str_truncate(dest, 0); - base64_encode(buf, max, str); - base64_decode(str_data(str), str_len(str), NULL, dest); - if (str_len(dest) != max && - memcmp(buf, str_data(dest), max) != 0) - break; - } - test_out("base64 random", success); -} - -struct test_base64_decode_output { - const char *text; - int ret; - unsigned int src_pos; -}; - -static void test_base64_decode(void) -{ - static const char *input[] = { - "\taGVsbG8gd29ybGQ=", - "\nZm9v\n \tIGJh \t\ncml0cw==", - " anVzdCBuaWlu \n", - "aGVsb", - "aGVsb!!!!!", - "aGVs!!!!!" - }; - static const struct test_base64_decode_output output[] = { - { "hello world", 0, -1 }, - { "foo barits", 0, -1 }, - { "just niin", 1, -1 }, - { "hel", 1, 4 }, - { "hel", -1, 4 }, - { "hel", -1, 4 } - }; - string_t *str; - unsigned int i; - size_t src_pos; - int ret; - bool success; - - str = t_str_new(256); - for (i = 0; i < N_ELEMENTS(input); i++) { - str_truncate(str, 0); - - src_pos = 0; - ret = base64_decode(input[i], strlen(input[i]), &src_pos, str); - - success = output[i].ret == ret && - strcmp(output[i].text, str_c(str)) == 0 && - (src_pos == output[i].src_pos || - (output[i].src_pos == (unsigned int)-1 && - src_pos == strlen(input[i]))); - test_out(t_strdup_printf("base64_decode(%d)", i), success); - } -} - -static int cmp_uint(const void *p1, const void *p2) -{ - const unsigned int *i1 = p1, *i2 = p2; - - return *i1 - *i2; -} - -static void test_bsearch_insert_pos(void) -{ - static const unsigned int input[] = { - 1, 5, 9, 15, 16, -1, - 1, 5, 9, 15, 16, 17, -1, - -1 - }; - static const unsigned int max_key = 18; - const unsigned int *cur; - unsigned int key, len, i, idx; - bool success; - - cur = input; - for (i = 0; cur[0] != -1U; i++) { - for (len = 0; cur[len] != -1U; len++) ; - for (key = 0; key < max_key; key++) { - if (bsearch_insert_pos(&key, cur, len, sizeof(*cur), - cmp_uint, &idx)) - success = cur[idx] == key; - else if (idx == 0) - success = cur[0] > key; - else if (idx == len) - success = cur[len-1] < key; - else { - success = cur[idx-1] < key && - cur[idx+1] > key; - } - if (!success) - break; - } - cur += len + 1; - - test_out(t_strdup_printf("bsearch_insert_pos(%d,%d)", i, key), - success); - } -} - -static void test_buffer(void) -{ -#define BUF_TEST_SIZE (1024*2) -#define BUF_TEST_COUNT 1000 - buffer_t *buf; - unsigned char *p, testdata[BUF_TEST_SIZE], shadowbuf[BUF_TEST_SIZE]; - unsigned int i, shadowbuf_size; - size_t pos, pos2, size; - int test = -1; - bool zero; - - buf = buffer_create_dynamic(default_pool, 1); - for (i = 0; i < BUF_TEST_SIZE; i++) - testdata[i] = random(); - memset(shadowbuf, 0, sizeof(shadowbuf)); - - srand(1); - shadowbuf_size = 0; - for (i = 0; i < BUF_TEST_COUNT; i++) { - if (buf->used == BUF_TEST_SIZE) { - size = shadowbuf_size = rand() % (buf->used - 1); - buffer_set_used_size(buf, size); - memset(shadowbuf + shadowbuf_size, 0, - BUF_TEST_SIZE - shadowbuf_size); - i_assert(buf->used < BUF_TEST_SIZE); - } - - test = rand() % 6; - zero = rand() % 10 == 0; - switch (test) { - case 0: - pos = rand() % (BUF_TEST_SIZE-1); - size = rand() % (BUF_TEST_SIZE - pos); - if (!zero) { - buffer_write(buf, pos, testdata, size); - memcpy(shadowbuf + pos, testdata, size); - } else { - buffer_write_zero(buf, pos, size); - memset(shadowbuf + pos, 0, size); - } - if (pos + size > shadowbuf_size) - shadowbuf_size = pos + size; - break; - case 1: - size = rand() % (BUF_TEST_SIZE - buf->used); - if (!zero) { - buffer_append(buf, testdata, size); - memcpy(shadowbuf + shadowbuf_size, - testdata, size); - } else { - buffer_append_zero(buf, size); - memset(shadowbuf + shadowbuf_size, 0, size); - } - shadowbuf_size += size; - break; - case 2: - pos = rand() % (BUF_TEST_SIZE-1); - size = rand() % (BUF_TEST_SIZE - I_MAX(buf->used, pos)); - if (!zero) { - buffer_insert(buf, pos, testdata, size); - memmove(shadowbuf + pos + size, - shadowbuf + pos, - BUF_TEST_SIZE - (pos + size)); - memcpy(shadowbuf + pos, testdata, size); - } else { - buffer_insert_zero(buf, pos, size); - memmove(shadowbuf + pos + size, - shadowbuf + pos, - BUF_TEST_SIZE - (pos + size)); - memset(shadowbuf + pos, 0, size); - } - if (pos < shadowbuf_size) - shadowbuf_size += size; - else - shadowbuf_size = pos + size; - break; - case 3: - pos = rand() % (BUF_TEST_SIZE-1); - size = rand() % (BUF_TEST_SIZE - pos); - buffer_delete(buf, pos, size); - if (pos < shadowbuf_size) { - if (pos + size > shadowbuf_size) - size = shadowbuf_size - pos; - memmove(shadowbuf + pos, - shadowbuf + pos + size, - BUF_TEST_SIZE - (pos + size)); - - shadowbuf_size -= size; - memset(shadowbuf + shadowbuf_size, 0, - BUF_TEST_SIZE - shadowbuf_size); - } - break; - case 4: - if (shadowbuf_size == 0) - break; - pos = rand() % (shadowbuf_size-1); /* dest */ - pos2 = rand() % (shadowbuf_size-1); /* source */ - size = rand() % (shadowbuf_size - I_MAX(pos, pos2)); - buffer_copy(buf, pos, buf, pos2, size); - memmove(shadowbuf + pos, - shadowbuf + pos2, size); - if (pos > pos2 && pos + size > shadowbuf_size) - shadowbuf_size = pos + size; - break; - case 5: - pos = rand() % (BUF_TEST_SIZE-1); - size = rand() % (BUF_TEST_SIZE - pos); - p = buffer_get_space_unsafe(buf, pos, size); - memcpy(p, testdata, size); - memcpy(shadowbuf + pos, testdata, size); - if (pos + size > shadowbuf_size) - shadowbuf_size = pos + size; - break; - } - i_assert(shadowbuf_size <= BUF_TEST_SIZE); - - if (buf->used != shadowbuf_size || - memcmp(buf->data, shadowbuf, buf->used) != 0) - break; - } - if (i == BUF_TEST_COUNT) - test_out("buffer", TRUE); - else { - test_out_reason("buffer", FALSE, - t_strdup_printf("round %u test %d failed", i, test)); - } - buffer_free(&buf); -} - -static bool aqueue_is_ok(struct aqueue *aqueue, unsigned int deleted_n) -{ - const unsigned int *p; - unsigned int n, i, count; - - count = aqueue_count(aqueue); - for (i = 0, n = 1; i < count; i++, n++) { - p = array_idx_i(aqueue->arr, aqueue_idx(aqueue, i)); - if (i == deleted_n) - n++; - if (*p != n) - return FALSE; - } - return TRUE; -} - -static const unsigned int aqueue_input[] = { 1, 2, 3, 4, 5, 6 }; -static const char *test_aqueue2(unsigned int initial_size) -{ - ARRAY_DEFINE(aqueue_array, unsigned int); - unsigned int i, j, k; - struct aqueue *aqueue; - - for (i = 0; i < N_ELEMENTS(aqueue_input); i++) { - for (k = 0; k < N_ELEMENTS(aqueue_input); k++) { - t_array_init(&aqueue_array, initial_size); - aqueue = aqueue_init(&aqueue_array.arr); - aqueue->head = aqueue->tail = initial_size - 1; - for (j = 0; j < k; j++) { - aqueue_append(aqueue, &aqueue_input[j]); - if (aqueue_count(aqueue) != j + 1) { - return t_strdup_printf("Wrong count after append %u vs %u)", - aqueue_count(aqueue), j + 1); - } - if (!aqueue_is_ok(aqueue, -1U)) - return "Invalid data after append"; - } - - if (k != 0 && i < k) { - aqueue_delete(aqueue, i); - if (aqueue_count(aqueue) != k - 1) - return "Wrong count after delete"; - if (!aqueue_is_ok(aqueue, i)) - return "Invalid data after delete"; - } - } - } - aqueue_clear(aqueue); - if (aqueue_count(aqueue) != 0) - return "aqueue_clear() broken"; - return NULL; -} - -static void test_aqueue(void) -{ - unsigned int i; - const char *reason = NULL; - - for (i = 1; i <= N_ELEMENTS(aqueue_input) + 1 && reason == NULL; i++) { - T_BEGIN { - reason = test_aqueue2(i); - } T_END; - } - test_out_reason("aqueue", reason == NULL, reason); -} - -static bool mem_has_bytes(const void *mem, size_t size, uint8_t b) -{ - const uint8_t *bytes = mem; - unsigned int i; - - for (i = 0; i < size; i++) { - if (bytes[i] != b) - return FALSE; - } - return TRUE; -} - -static void test_mempool_alloconly(void) -{ -#define PMALLOC_MAX_COUNT 128 - pool_t pool; - unsigned int i, j, k; - void *mem[PMALLOC_MAX_COUNT + 1]; - bool success = TRUE; - - for (i = 0; i < 64; i++) { - for (j = 1; j <= 128; j++) { - pool = pool_alloconly_create(MEMPOOL_GROWING"test", i); - mem[0] = p_malloc(pool, j); - memset(mem[0], j, j); - - for (k = 1; k <= PMALLOC_MAX_COUNT; k++) { - mem[k] = p_malloc(pool, k); - memset(mem[k], k, k); - } - - if (!mem_has_bytes(mem[0], j, j)) - success = FALSE; - for (k = 1; k <= PMALLOC_MAX_COUNT; k++) { - if (!mem_has_bytes(mem[k], k, k)) - success = FALSE; - } - pool_unref(&pool); - } - } - test_out("mempool_alloconly", success); -} - -struct test_net_is_in_network_input { - const char *ip; - const char *net; - unsigned int bits; - bool ret; -}; - -static void test_net_is_in_network(void) -{ - static struct test_net_is_in_network_input input[] = { - { "1.2.3.4", "1.2.3.4", 32, TRUE }, - { "1.2.3.4", "1.2.3.3", 32, FALSE }, - { "1.2.3.4", "1.2.3.5", 32, FALSE }, - { "1.2.3.4", "1.2.2.4", 32, FALSE }, - { "1.2.3.4", "1.1.3.4", 32, FALSE }, - { "1.2.3.4", "0.2.3.4", 32, FALSE }, - { "1.2.3.253", "1.2.3.254", 31, FALSE }, - { "1.2.3.254", "1.2.3.254", 31, TRUE }, - { "1.2.3.255", "1.2.3.254", 31, TRUE }, - { "1.2.3.255", "1.2.3.0", 24, TRUE }, - { "1.2.255.255", "1.2.254.0", 23, TRUE }, - { "255.255.255.255", "128.0.0.0", 1, TRUE }, - { "255.255.255.255", "127.0.0.0", 1, FALSE } -#ifdef HAVE_IPV6 - , - { "1234:5678::abcf", "1234:5678::abce", 127, TRUE }, - { "1234:5678::abcd", "1234:5678::abce", 127, FALSE }, - { "123e::ffff", "123e::0", 15, TRUE }, - { "123d::ffff", "123e::0", 15, FALSE } -#endif - }; - struct ip_addr ip, net_ip; - unsigned int i; - bool success; - - for (i = 0; i < N_ELEMENTS(input); i++) { - net_addr2ip(input[i].ip, &ip); - net_addr2ip(input[i].net, &net_ip); - success = net_is_in_network(&ip, &net_ip, input[i].bits) == - input[i].ret; - test_out(t_strdup_printf("net_is_in_network(%u)", i), success); - } -} - -struct pq_test_item { - struct priorityq_item item; - int num; -}; - -static int cmp_int(const void *p1, const void *p2) -{ - const struct pq_test_item *i1 = p1, *i2 = p2; - - return i1->num - i2->num; -} - -static void test_primes(void) -{ - unsigned int i, j, num; - bool success; - - success = primes_closest(0) > 0; - for (num = 1; num < 1024; num++) { - if (primes_closest(num) < num) - success = FALSE; - } - for (i = 10; i < 32; i++) { - num = (1 << i) - 100; - for (j = 0; j < 200; j++, num++) { - if (primes_closest(num) < num) - success = FALSE; - } - } - test_out("primes_closest()", success); -} - -static void test_priorityq(void) -{ -#define PQ_MAX_ITEMS 100 - static const int input[] = { - 1, 2, 3, 4, 5, 6, 7, 8, -1, - 8, 7, 6, 5, 4, 3, 2, 1, -1, - 8, 7, 5, 6, 1, 3, 4, 2, -1, - -1 - }; - static const int output[] = { - 1, 2, 3, 4, 5, 6, 7, 8 - }; - struct pq_test_item *item, items[PQ_MAX_ITEMS]; - unsigned int i, j; - struct priorityq *pq; - pool_t pool; - int prev; - bool success = TRUE; - - pool = pool_alloconly_create("priorityq items", 1024); - - /* simple tests with popping only */ - for (i = 0; input[i] != -1; i++) { - p_clear(pool); - pq = priorityq_init(cmp_int, 1); - for (j = 0; input[i] != -1; i++, j++) { - if (priorityq_count(pq) != j) - success = FALSE; - item = p_new(pool, struct pq_test_item, 1); - item->num = input[i]; - priorityq_add(pq, &item->item); - } - for (j = 0; j < N_ELEMENTS(output); j++) { - if (priorityq_count(pq) != N_ELEMENTS(output) - j) - success = FALSE; - - item = (struct pq_test_item *)priorityq_peek(pq); - if (output[j] != item->num) - success = FALSE; - item = (struct pq_test_item *)priorityq_pop(pq); - if (output[j] != item->num) - success = FALSE; - } - if (priorityq_count(pq) != 0) - success = FALSE; - if (priorityq_peek(pq) != NULL || priorityq_pop(pq) != NULL) - success = FALSE; - priorityq_deinit(&pq); - } - test_out("priorityq(1)", success); - - /* randomized tests, remove elements */ - success = TRUE; - for (i = 0; i < 100; i++) { - pq = priorityq_init(cmp_int, 1); - for (j = 0; j < PQ_MAX_ITEMS; j++) { - items[j].num = rand(); - priorityq_add(pq, &items[j].item); - } - for (j = 0; j < PQ_MAX_ITEMS; j++) { - if (rand() % 3 == 0) { - priorityq_remove(pq, &items[j].item); - items[j].num = -1; - } - } - prev = 0; - while (priorityq_count(pq) > 0) { - item = (struct pq_test_item *)priorityq_pop(pq); - if (item->num < 0 || prev > item->num) - success = FALSE; - prev = item->num; - item->num = -1; - } - for (j = 0; j < PQ_MAX_ITEMS; j++) { - if (items[j].num != -1) - success = FALSE; - } - priorityq_deinit(&pq); - } - test_out("priorityq(2)", success); - pool_unref(&pool); -} - -static void test_seq_range_array_random(void) -{ -#define SEQ_RANGE_TEST_BUFSIZE 20 -#define SEQ_RANGE_TEST_COUNT 10000 - unsigned char shadowbuf[SEQ_RANGE_TEST_BUFSIZE]; - ARRAY_TYPE(seq_range) range; - const struct seq_range *seqs; - uint32_t seq1, seq2; - unsigned int i, j, ret, ret2, count; - int test = -1; - - ret = ret2 = 0; - i_array_init(&range, 1); - memset(shadowbuf, 0, sizeof(shadowbuf)); - for (i = 0; i < SEQ_RANGE_TEST_COUNT; i++) { - seq1 = rand() % SEQ_RANGE_TEST_BUFSIZE; - seq2 = seq1 + rand() % (SEQ_RANGE_TEST_BUFSIZE - seq1); - test = rand() % 4; - switch (test) { - case 0: - seq_range_array_add(&range, 0, seq1); - shadowbuf[seq1] = 1; - break; - case 1: - seq_range_array_add_range(&range, seq1, seq2); - memset(shadowbuf + seq1, 1, seq2 - seq1 + 1); - break; - case 2: - ret = seq_range_array_remove(&range, seq1) ? 1 : 0; - ret2 = shadowbuf[seq1] != 0 ? 1 : 0; - shadowbuf[seq1] = 0; - break; - case 3: - ret = seq_range_array_remove_range(&range, seq1, seq2); - for (ret2 = 0; seq1 <= seq2; seq1++) { - if (shadowbuf[seq1] != 0) { - ret2++; - shadowbuf[seq1] = 0; - } - } - break; - } - if (ret != ret2) - break; - - seqs = array_get(&range, &count); - for (j = 0, seq1 = 0; j < count; j++) { - if (j > 0 && seqs[j-1].seq2 >= seqs[j].seq1) - goto fail; - for (; seq1 < seqs[j].seq1; seq1++) { - if (shadowbuf[seq1] != 0) - goto fail; - } - for (; seq1 <= seqs[j].seq2; seq1++) { - if (shadowbuf[seq1] == 0) - goto fail; - } - } - i_assert(seq1 <= SEQ_RANGE_TEST_BUFSIZE); - for (; seq1 < SEQ_RANGE_TEST_BUFSIZE; seq1++) { - if (shadowbuf[seq1] != 0) - goto fail; - } - } -fail: - if (i == SEQ_RANGE_TEST_COUNT) - test_out("seq_range_array random", TRUE); - else { - test_out_reason("seq_range_array random", FALSE, - t_strdup_printf("round %u test %d failed", i, test)); - } -} - -static void test_seq_range_array_invert(void) -{ - static const unsigned int input_min = 1, input_max = 5; - static const unsigned int input[] = { - 1, 2, 3, 4, 5, -1U, - 2, 3, 4, -1U, - 1, 2, 4, 5, -1U, - 1, 3, 5, -1U, - 1, -1U, - 5, -1U, - -1U - }; - ARRAY_TYPE(seq_range) range = ARRAY_INIT; - unsigned int i, j, seq, start, num; - bool old_exists, success; - - for (i = num = 0; input[i] != -1U; num++, i++) { - success = TRUE; - start = i; - for (; input[i] != -1U; i++) { - seq_range_array_add(&range, 32, input[i]); - for (j = start; j < i; j++) { - if (!seq_range_exists(&range, input[j])) - success = FALSE; - } - } - - seq_range_array_invert(&range, input_min, input_max); - for (seq = input_min; seq <= input_max; seq++) { - for (j = start; input[j] != -1U; j++) { - if (input[j] == seq) - break; - } - old_exists = input[j] != -1U; - if (seq_range_exists(&range, seq) == old_exists) - success = FALSE; - } - test_out(t_strdup_printf("seq_range_array_invert(%u)", num), - success); - array_free(&range); - } -} - -static void test_seq_range_create(ARRAY_TYPE(seq_range) *array, uint8_t byte) -{ - unsigned int i; - - array_clear(array); - for (i = 0; i < 8; i++) { - if ((byte & (1 << i)) != 0) - seq_range_array_add(array, 0, i + 1); - } -} - -static void test_seq_range_array_have_common(void) -{ - ARRAY_TYPE(seq_range) arr1, arr2; - unsigned int i, j; - bool ret1, ret2, success = TRUE; - - t_array_init(&arr1, 8); - t_array_init(&arr2, 8); - for (i = 0; i < 256; i++) { - test_seq_range_create(&arr1, i); - for (j = 0; j < 256; j++) { - test_seq_range_create(&arr2, j); - ret1 = seq_range_array_have_common(&arr1, &arr2); - ret2 = (i & j) != 0; - if (ret1 != ret2) - success = FALSE; - } - } - test_out("seq_range_array_have_common()", success); -} - -static void test_seq_range_array(void) -{ - test_seq_range_array_invert(); - test_seq_range_array_have_common(); - test_seq_range_array_random(); -} - -static const char *str_find_text = "xababcd"; - -static bool test_str_find_substring(const char *key, int expected_pos) -{ - const unsigned char *text = (const unsigned char *)str_find_text; - const unsigned int text_len = strlen(str_find_text); - struct str_find_context *ctx; - unsigned int i, j, pos, max, offset; - bool ret; - - ctx = str_find_init(pool_datastack_create(), key); - /* divide text into every possible block combination and test that - it matches */ - max = 1 << (text_len-1); - for (i = 0; i < max; i++) { - str_find_reset(ctx); - pos = 0; offset = 0; ret = FALSE; - for (j = 0; j < text_len; j++) { - if ((i & (1 << j)) != 0) { - if (str_find_more(ctx, text+pos, j-pos+1)) { - ret = TRUE; - break; - } - offset += j-pos + 1; - pos = j + 1; - } - } - if (pos != text_len && !ret) { - if (str_find_more(ctx, text+pos, j-pos)) - ret = TRUE; - } - if (expected_pos < 0) { - if (ret) - return FALSE; - } else { - if (!ret) - return FALSE; - - pos = str_find_get_match_end_pos(ctx) + - offset - strlen(key); - if ((int)pos != expected_pos) - return FALSE; - } - } - return TRUE; -} - -struct str_find_input { - const char *str; - int pos; -}; -static void test_str_find(void) -{ - static const char *fail_input[] = { - "xabc", - "xabd", - "abd" - }; - unsigned int idx, len; - const char *key, *p; - unsigned int i; - bool success = TRUE; - - for (idx = 0; idx < strlen(str_find_text); idx++) { - for (len = strlen(str_find_text)-idx; len > 0; len--) { - /* we'll get a search key for all substrings of text */ - T_BEGIN { - key = t_strndup(str_find_text + idx, len); - p = strstr(str_find_text, key); - success = test_str_find_substring(key, p - str_find_text); - } T_END; - if (!success) - break; - } - } - for (i = 0; i < N_ELEMENTS(fail_input) && success; i++) - success = test_str_find_substring(fail_input[i], -1); - test_out("str_find()", success); -} - -struct str_sanitize_input { - const char *str; - unsigned int max_len; -}; -static void test_str_sanitize(void) -{ - static struct str_sanitize_input input[] = { - { NULL, 2 }, - { "", 2 }, - { "a", 2 }, - { "ab", 2 }, - { "abc", 2 }, - { "abcd", 3 }, - { "abcde", 4 } - }; - static const char *output[] = { - NULL, - "", - "a", - "ab", - "...", - "...", - "a..." - }; - const char *str; - unsigned int i; - bool success; - - for (i = 0; i < N_ELEMENTS(input); i++) { - str = str_sanitize(input[i].str, input[i].max_len); - success = null_strcmp(output[i], str) == 0; - test_out(t_strdup_printf("str_sanitize(%d)", i), success); - } -} - -struct test_message_date_output { - time_t time; - int tz_offset; - bool ret; -}; - -struct test_utc_mktime_input { - int year, month, day, hour, min, sec; -}; - -static void test_utc_mktime(void) -{ - static struct test_utc_mktime_input input[] = { -#ifdef TIME_T_SIGNED - { 1969, 12, 31, 23, 59, 59 }, - { 1901, 12, 13, 20, 45, 53 }, -#endif -#if (TIME_T_MAX_BITS > 32 || !defined(TIME_T_SIGNED)) - { 2106, 2, 7, 6, 28, 15 }, -#endif - { 2007, 11, 7, 1, 7, 20 }, - { 1970, 1, 1, 0, 0, 0 }, - { 2038, 1, 19, 3, 14, 7 } - }; - static time_t output[] = { -#ifdef TIME_T_SIGNED - -1, - -2147483647, -#endif -#if (TIME_T_MAX_BITS > 32 || !defined(TIME_T_SIGNED)) - 4294967295, -#endif - 1194397640, - 0, - 2147483647 - }; - struct tm tm; - unsigned int i; - time_t t; - bool success; - - for (i = 0; i < N_ELEMENTS(input); i++) { - memset(&tm, 0, sizeof(tm)); - tm.tm_year = input[i].year - 1900; - tm.tm_mon = input[i].month - 1; - tm.tm_mday = input[i].day; - tm.tm_hour = input[i].hour; - tm.tm_min = input[i].min; - tm.tm_sec = input[i].sec; - - t = utc_mktime(&tm); - success = t == output[i]; - test_out_reason(t_strdup_printf("utc_mktime(%d)", i), success, - success ? NULL : t_strdup_printf("%ld != %ld", - (long)t, (long)output[i])); - } -} - -int main(void) -{ - static void (*test_functions[])(void) = { - test_array, - test_aqueue, - test_base64_encode, - test_base64_decode, - test_bsearch_insert_pos, - test_buffer, - test_mempool_alloconly, - test_net_is_in_network, - test_primes, - test_priorityq, - test_seq_range_array, - test_str_find, - test_str_sanitize, - test_utc_mktime, - - test_istreams - }; - unsigned int i; - - test_init(); - for (i = 0; i < N_ELEMENTS(test_functions); i++) { - T_BEGIN { - test_functions[i](); - } T_END; - } - return test_deinit(); -} diff -r 2090983d37fa -r 665ea7a8d26e src/tests/test-lib.h --- a/src/tests/test-lib.h Sat May 09 16:10:36 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -#include "lib.h" -#include "test-common.h" - -void test_istreams(void); diff -r 2090983d37fa -r 665ea7a8d26e src/tests/test-mail.c --- a/src/tests/test-mail.c Sat May 09 16:10:36 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,367 +0,0 @@ -/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */ - -#include "lib.h" -#include "str.h" -#include "istream.h" -#include "rfc822-parser.h" -#include "rfc2231-parser.h" -#include "message-address.h" -#include "message-date.h" -#include "message-parser.h" -#include "istream-header-filter.h" -#include "test-common.h" - -static const char test_msg[] = -"Return-Path: \n" -"Subject: Hello world\n" -"From: Test User \n" -"To: Another User \n" -"Message-Id: <1.2.3.4@example>\n" -"Mime-Version: 1.0\n" -"Date: Sun, 23 May 2007 04:58:08 +0300\n" -"Content-Type: multipart/signed; micalg=pgp-sha1;\n" -" protocol=\"application/pgp-signature\";\n" -" boundary=\"=-GNQXLhuj24Pl1aCkk4/d\"\n" -"\n" -"--=-GNQXLhuj24Pl1aCkk4/d\n" -"Content-Type: text/plain\n" -"Content-Transfer-Encoding: quoted-printable\n" -"\n" -"There was a day=20\n" -"a happy=20day\n" -"\n" -"--=-GNQXLhuj24Pl1aCkk4/d\n" -"Content-Type: application/pgp-signature; name=signature.asc\n" -"\n" -"-----BEGIN PGP SIGNATURE-----\n" -"Version: GnuPG v1.2.4 (GNU/Linux)\n" -"\n" -"invalid\n" -"-----END PGP SIGNATURE-----\n" -"\n" -"--=-GNQXLhuj24Pl1aCkk4/d--\n" -"\n" -"\n"; -#define TEST_MSG_LEN (sizeof(test_msg)-1) - -static bool cmp_addr(const struct message_address *a1, - const struct message_address *a2) -{ - return null_strcmp(a1->name, a2->name) == 0 && - null_strcmp(a1->route, a2->route) == 0 && - null_strcmp(a1->mailbox, a2->mailbox) == 0 && - null_strcmp(a1->domain, a2->domain) == 0 && - a1->invalid_syntax == a2->invalid_syntax; -} - -static void test_message_address(void) -{ - static const char *input[] = { - "user@domain", - "", - "foo bar ", - "\"foo bar\" ", - "<@route:user@domain>", - "<@route@route2:user@domain>", - "hello <@route ,@route2:user@domain>", - "user (hello)", - "hello ", - "@domain" - }; - static struct message_address group_prefix = { - NULL, NULL, NULL, "group", NULL, FALSE - }; - static struct message_address group_suffix = { - NULL, NULL, NULL, NULL, NULL, FALSE - }; - static struct message_address output[] = { - { NULL, NULL, NULL, "user", "domain", FALSE }, - { NULL, NULL, NULL, "user", "domain", FALSE }, - { NULL, "foo bar", NULL, "user", "domain", FALSE }, - { NULL, "foo bar", NULL, "user", "domain", FALSE }, - { NULL, NULL, "@route", "user", "domain", FALSE }, - { NULL, NULL, "@route,@route2", "user", "domain", FALSE }, - { NULL, "hello", "@route,@route2", "user", "domain", FALSE }, - { NULL, "hello", NULL, "user", "", TRUE }, - { NULL, "hello", NULL, "user", "", TRUE }, - { NULL, NULL, NULL, "", "domain", TRUE } - }; - struct message_address *addr; - string_t *group; - unsigned int i; - bool success; - - group = t_str_new(256); - str_append(group, "group: "); - - for (i = 0; i < N_ELEMENTS(input); i++) { - addr = message_address_parse(pool_datastack_create(), - (const unsigned char *)input[i], - strlen(input[i]), -1U, FALSE); - success = addr != NULL && addr->next == NULL && - cmp_addr(addr, &output[i]); - test_out(t_strdup_printf("message_address_parse(%d)", i), - success); - - if (!output[i].invalid_syntax) { - if (i != 0) { - if ((i % 2) == 0) - str_append(group, ","); - else - str_append(group, " , \n "); - } - str_append(group, input[i]); - } - } - str_append_c(group, ';'); - - addr = message_address_parse(pool_datastack_create(), str_data(group), - str_len(group), -1U, FALSE); - success = addr != NULL && cmp_addr(addr, &group_prefix); - addr = addr->next; - for (i = 0; i < N_ELEMENTS(input) && addr != NULL; i++) { - if (output[i].invalid_syntax) - continue; - if (!cmp_addr(addr, &output[i])) { - success = FALSE; - break; - } - addr = addr->next; - } - if (addr == NULL || addr->next != NULL || - !cmp_addr(addr, &group_suffix)) - success = FALSE; - test_out("message_address_parse(group)", success); -} - -struct test_message_date_output { - time_t time; - int tz_offset; - bool ret; -}; - -static void test_message_date_parse(void) -{ - static const char *input[] = { -#ifdef TIME_T_SIGNED - "Thu, 01 Jan 1970 01:59:59 +0200", - "Fri, 13 Dec 1901 20:45:53 +0000", -#endif -#if (TIME_T_MAX_BITS > 32 || !defined(TIME_T_SIGNED)) - "Sun, 07 Feb 2106 06:28:15 +0000", -#endif - "Wed, 07 Nov 2007 01:07:20 +0200", - "Wed, 07 Nov 2007 01:07:20", - "Thu, 01 Jan 1970 02:00:00 +0200", - "Tue, 19 Jan 2038 03:14:07 +0000", - "Tue, 19 Jan 2038" - }; - static struct test_message_date_output output[] = { -#ifdef TIME_T_SIGNED - { -1, 2*60, TRUE }, - { -2147483647, 0, TRUE }, -#endif -#if (TIME_T_MAX_BITS > 32 || !defined(TIME_T_SIGNED)) - { 4294967295, 0, TRUE }, -#endif - { 1194390440, 2*60, TRUE }, - { 1194397640, 0, TRUE }, - { 0, 2*60, TRUE }, - { 2147483647, 0, TRUE }, - { 0, 0, FALSE } - }; - unsigned int i; - bool success; - time_t t; - int tz; - bool ret; - - for (i = 0; i < N_ELEMENTS(input); i++) { - ret = message_date_parse((const unsigned char *)input[i], - strlen(input[i]), &t, &tz); - success = (!ret && !output[i].ret) || - (ret == output[i].ret && t == output[i].time && - tz == output[i].tz_offset); - test_out(t_strdup_printf("message_date_parse(%d)", i), success); - } -} - -static bool msg_parts_cmp(struct message_part *p1, struct message_part *p2) -{ - while (p1 != NULL || p2 != NULL) { - if ((p1 != NULL) != (p2 != NULL)) - return FALSE; - if ((p1->children != NULL) != (p2->children != NULL)) - return FALSE; - - if (p1->children) { - if (!msg_parts_cmp(p1->children, p2->children)) - return FALSE; - } - - if (p1->physical_pos != p2->physical_pos || - p1->header_size.physical_size != p2->header_size.physical_size || - p1->header_size.virtual_size != p2->header_size.virtual_size || - p1->header_size.lines != p2->header_size.lines || - p1->body_size.physical_size != p2->body_size.physical_size || - p1->body_size.virtual_size != p2->body_size.virtual_size || - p1->body_size.lines != p2->body_size.lines || - p1->flags != p2->flags) - return FALSE; - - p1 = p1->next; - p2 = p2->next; - } - return TRUE; -} - -static void test_message_parser(void) -{ - struct message_parser_ctx *parser; - struct istream *input; - struct message_part *parts, *parts2; - struct message_block block; - unsigned int i; - bool success = TRUE; - pool_t pool; - int ret; - - pool = pool_alloconly_create("message parser", 10240); - input = i_stream_create_from_data(test_msg, TEST_MSG_LEN); - - parser = message_parser_init(pool, input, 0, 0); - while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; - i_assert(ret < 0); - ret = message_parser_deinit(&parser, &parts); - i_assert(ret == 0); - i_stream_unref(&input); - - input = test_istream_create(test_msg); - test_istream_set_allow_eof(input, FALSE); - - parser = message_parser_init(pool, input, 0, 0); - for (i = 1; i <= TEST_MSG_LEN*2+1; i++) { - test_istream_set_size(input, i/2); - if (i > TEST_MSG_LEN*2) - test_istream_set_allow_eof(input, TRUE); - while ((ret = message_parser_parse_next_block(parser, - &block)) > 0) ; - if (ret < 0 && i < TEST_MSG_LEN*2) { - success = FALSE; - break; - } - } - ret = message_parser_deinit(&parser, &parts2); - i_assert(ret == 0); - i_stream_unref(&input); - - if (!msg_parts_cmp(parts, parts2)) - success = FALSE; - - pool_unref(&pool); - test_out("message_parser()", success); -} - -static void test_rfc2231_parser(void) -{ - const char *input = - "; key*2=ba%" - "; key2*0=a" - "; key3*0*=us-ascii'en'xyz" - "; key*0=\"foo\"" - "; key2*1*=b%25" - "; key3*1=plop%" - "; key*1=baz"; - const char *output[] = { - "key", - "foobazba%", - "key2*", - "''ab%25", - "key3*", - "us-ascii'en'xyzplop%25", - NULL - }; - struct rfc822_parser_context parser; - const char *const *result; - unsigned int i; - bool success; - - rfc822_parser_init(&parser, (const void *)input, strlen(input), NULL); - if (rfc2231_parse(&parser, &result) < 0) - success = FALSE; - else { - success = TRUE; - for (i = 0; output[i] != NULL && result[i] != NULL; i++) { - if (strcmp(output[i], result[i]) != 0) - break; - } - if (output[i] != NULL || result[i] != NULL) - success = FALSE; - } - test_out("rfc2231_parse()", success); -} - -static void filter_callback(struct message_header_line *hdr, - bool *matched, void *context ATTR_UNUSED) -{ - if (hdr != NULL && hdr->name_offset == 0) { - /* drop first header */ - *matched = TRUE; - } -} - -static void test_istream_filter(void) -{ - static const char *exclude_headers[] = { "To", NULL }; - const char *input = "From: foo\nFrom: abc\nTo: bar\n\nhello world\n"; - const char *output = "From: abc\n\nhello world\n"; - struct istream *istream, *filter; - unsigned int i, input_len = strlen(input); - unsigned int output_len = strlen(output); - const unsigned char *data; - size_t size; - ssize_t ret; - bool success = TRUE; - - istream = test_istream_create(input); - filter = i_stream_create_header_filter(istream, - HEADER_FILTER_EXCLUDE | - HEADER_FILTER_NO_CR, - exclude_headers, 1, - filter_callback, NULL); - for (i = 1; i <= input_len; i++) { - test_istream_set_size(istream, i); - ret = i_stream_read(filter); - if (ret < 0) { - success = FALSE; - break; - } - } - data = i_stream_get_data(filter, &size); - if (size != output_len || memcmp(data, output, size) != 0) - success = FALSE; - - i_stream_skip(filter, size); - i_stream_seek(filter, 0); - while ((ret = i_stream_read(filter)) > 0) ; - data = i_stream_get_data(filter, &size); - if (size != output_len || memcmp(data, output, size) != 0) - success = FALSE; - - i_stream_unref(&filter); - i_stream_unref(&istream); - - test_out("i_stream_create_header_filter()", success); -} - -int main(void) -{ - test_init(); - - test_message_address(); - test_message_date_parse(); - test_message_parser(); - test_rfc2231_parser(); - test_istream_filter(); - return test_deinit(); -}