view src/tests/test-mail.c @ 6727:c49084d2148e HEAD

Added message-parser unit test.
author Timo Sirainen <tss@iki.fi>
date Thu, 08 Nov 2007 03:08:03 +0200
parents 4e4a5d6bb2cb
children d329f3c77c65
line wrap: on
line source

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

#include "lib.h"
#include "str.h"
#include "istream-internal.h"
#include "message-address.h"
#include "message-date.h"
#include "message-parser.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;
}

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>"
	};
	static struct message_address group_prefix = {
		NULL, NULL, NULL, "group", NULL
	};
	static struct message_address group_suffix = {
		NULL, NULL, NULL, NULL, NULL
	};
	static struct message_address output[] = {
		{ NULL, NULL, NULL, "user", "domain" },
		{ NULL, NULL, NULL, "user", "domain" },
		{ NULL, "foo bar", NULL, "user", "domain" },
		{ NULL, "foo bar", NULL, "user", "domain" },
		{ NULL, NULL, "@route", "user", "domain" },
		{ NULL, NULL, "@route,@route2", "user", "domain" },
		{ NULL, "hello", "@route,@route2", "user", "domain" }
	};
	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 (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 (!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:52 +0000",
#endif
#if TIME_T_MAX_BITS > 31
		"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 },
		{ -2147483648, 0, TRUE },
#endif
#if TIME_T_MAX_BITS > 31
		{ 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 ssize_t test_read(struct istream_private *stream)
{
	if (stream->pos < TEST_MSG_LEN)
		return 0;

	stream->istream.eof = TRUE;
	return -1;
}

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);
	parts = message_parser_deinit(&parser);
	i_stream_unref(&input);

	input = i_stream_create_from_data(test_msg, TEST_MSG_LEN);
	input->blocking = FALSE;
	input->real_stream->read = test_read;

	parser = message_parser_init(pool, input, 0, 0);
	for (i = 1; i <= TEST_MSG_LEN; i++) {
		input->real_stream->pos = i;
		while ((ret = message_parser_parse_next_block(parser,
							      &block)) > 0) ;
		if (ret < 0 && i < TEST_MSG_LEN) {
			success = FALSE;
			break;
		}
	}
	parts2 = message_parser_deinit(&parser);
	i_stream_unref(&input);

	if (!msg_parts_cmp(parts, parts2))
		success = FALSE;

	pool_unref(&pool);
	test_out("message_parser()", success);
}

int main(void)
{
	test_init();

	test_message_address();
	test_message_date_parse();
	test_message_parser();
	return test_deinit();
}