changeset 9258:665ea7a8d26e HEAD

Tests are now run on "make check". Added initial tests for lib-index. Moved old tests away from tests/ to libraries' individual directories.
author Timo Sirainen <tss@iki.fi>
date Sat, 09 May 2009 16:20:41 -0400
parents 2090983d37fa
children 8fc3639ef601
files .hgignore configure.in src/Makefile.am src/lib-imap/Makefile.am src/lib-imap/test-imap.c src/lib-index/Makefile.am src/lib-index/mail-index-transaction-export.c src/lib-index/mail-index-transaction.c src/lib-index/mail-transaction-log-append.c src/lib-index/mail-transaction-log.h src/lib-index/test-index.c src/lib-index/test-index.h src/lib-index/test-transaction-log-view.c src/lib-mail/Makefile.am src/lib-mail/test-mail.c src/lib-test/Makefile.am src/lib-test/test-common.c src/lib-test/test-common.h src/lib-test/test-common.lo src/lib-test/test-common.o src/lib/Makefile.am src/lib/test-istream.c src/lib/test-lib.c src/lib/test-lib.h src/tests/Makefile.am src/tests/test-common.c src/tests/test-common.h src/tests/test-imap.c src/tests/test-istream.c src/tests/test-lib.c src/tests/test-lib.h src/tests/test-mail.c
diffstat 32 files changed, 2065 insertions(+), 1750 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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
--- 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
--- 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
--- /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);
+}
--- 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
--- 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 *
--- 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) {
--- 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;
--- 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
--- /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);
+}
--- /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
--- /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();
+}
--- 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
--- /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: <test@example.org>\n"
+"Subject: Hello world\n"
+"From: Test User <test@example.org>\n"
+"To: Another User <test2@example.org>\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",
+		"<user@domain>",
+		"foo bar <user@domain>",
+		"\"foo bar\" <user@domain>",
+		"<@route:user@domain>",
+		"<@route@route2:user@domain>",
+		"hello <@route ,@route2:user@domain>",
+		"user (hello)",
+		"hello <user>",
+		"@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);
+}
--- /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
--- /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 <stdio.h>
+
+#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();
+}
--- /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
--- /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'
+
Binary file src/lib-test/test-common.o has changed
--- 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
--- /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();
+}
--- /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 <stdlib.h>
+#include <time.h>
+
+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);
+}
--- /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);
--- 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)
--- 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 <stdio.h>
-
-#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;
-}
--- 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
--- 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();
-}
--- 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();
-}
--- 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 <stdlib.h>
-#include <time.h>
-
-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();
-}
--- 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);
--- 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: <test@example.org>\n"
-"Subject: Hello world\n"
-"From: Test User <test@example.org>\n"
-"To: Another User <test2@example.org>\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",
-		"<user@domain>",
-		"foo bar <user@domain>",
-		"\"foo bar\" <user@domain>",
-		"<@route:user@domain>",
-		"<@route@route2:user@domain>",
-		"hello <@route ,@route2:user@domain>",
-		"user (hello)",
-		"hello <user>",
-		"@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();
-}