/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */

#include "test-lib.h"
#include "test-common.h"
#include "iso8601-date.h"

#include <time.h>

struct iso8601_date_test {
	const char *date_in;
	const char *date_out;

	struct tm tm;
	int zone_offset;
};

/* Valid date tests */
struct iso8601_date_test valid_date_tests[] = {
	{
		.date_in = "2007-11-07T23:05:34+00:00",
		.tm = {
			.tm_year = 107, .tm_mon = 10, .tm_mday = 7,
			.tm_hour = 23, .tm_min = 5, .tm_sec = 34 },
	},{
		.date_in = "2011-01-07T21:03:31+00:30",
		.tm = {
			.tm_year = 111, .tm_mon = 0, .tm_mday = 7,
			.tm_hour = 21, .tm_min = 3, .tm_sec = 31 },
		.zone_offset = 30
	},{
		.date_in = "2006-05-09T18:04:12+05:30",
		.tm = {
			.tm_year = 106, .tm_mon = 4, .tm_mday = 9,
			.tm_hour = 18, .tm_min = 4, .tm_sec = 12 },
		.zone_offset = 5*60+30
	},{
		.date_in = "1975-10-30T06:33:29Z",
		.date_out = "1975-10-30T06:33:29+00:00",
		.tm = {
			.tm_year = 75, .tm_mon = 9, .tm_mday = 30,
			.tm_hour = 6, .tm_min = 33, .tm_sec = 29 },
	},{
		.date_in = "1988-04-24t15:02:12z",
		.date_out = "1988-04-24T15:02:12+00:00",
		.tm = {
			.tm_year = 88, .tm_mon = 3, .tm_mday = 24,
			.tm_hour = 15, .tm_min = 2, .tm_sec = 12 },
	},{
		.date_in = "2012-02-29T08:12:34.23198Z",
		.date_out = "2012-02-29T08:12:34+00:00",
		.tm = {
			.tm_year = 112, .tm_mon = 1, .tm_mday = 29,
			.tm_hour = 8, .tm_min = 12, .tm_sec = 34 },
	}
};

unsigned int valid_date_test_count = N_ELEMENTS(valid_date_tests);

static void test_iso8601_date_valid(void)
{
	unsigned int i;

	for (i = 0; i < valid_date_test_count; i++) T_BEGIN {
		const char *date_in, *date_out, *pdate_out;
		struct tm *tm = &valid_date_tests[i].tm, ptm;
		int zone_offset = valid_date_tests[i].zone_offset, pzone_offset;
		bool result;

		date_in = valid_date_tests[i].date_in;
		date_out = valid_date_tests[i].date_out == NULL ?
			date_in : valid_date_tests[i].date_out;

		test_begin(t_strdup_printf("iso8601 date valid [%d]", i));

		result = iso8601_date_parse_tm
			((const unsigned char *)date_in, strlen(date_in), &ptm, &pzone_offset);
		test_out(t_strdup_printf("parse %s", date_in), result);
		if (result) {
			bool equal = tm->tm_year == ptm.tm_year && tm->tm_mon == ptm.tm_mon &&
				tm->tm_mday == ptm.tm_mday && tm->tm_hour == ptm.tm_hour &&
				tm->tm_min == ptm.tm_min && tm->tm_sec == ptm.tm_sec;

			test_out("valid timestamp", equal);
			test_out_reason("valid timezone", zone_offset == pzone_offset,
				t_strdup_printf("%d", pzone_offset));

			pdate_out = iso8601_date_create_tm(tm, zone_offset);
			test_out_reason("valid create", strcmp(date_out, pdate_out) == 0,
				pdate_out);
		}

		test_end();
	} T_END;
}

/* Invalid date tests */
const char *invalid_date_tests[] = {
	"200-11-17T23:05:34+00:00",
	"2007:11-17T23:05:34+00:00",
	"2007-11?17T23:05:34+00:00",
	"2007-49-17T23:05:34+00:00",
	"2007-11-77T23:05:34+00:00",
	"2007-11-17K23:05:34+00:00",
	"2007-11-13T59:05:34+00:00",
	"2007-112-13T12:15:34+00:00",
	"2007-11-133T12:15:34+00:00",
	"2007-11-13T12J15:34+00:00",
	"2007-11-13T12:15*34+00:00",
	"2007-11-13T12:15:34/00:00",
	"2007-11-13T12:15:34+00-00",
	"2007-11-13T123:15:34+00:00",
	"2007-11-13T12:157:34+00:00",
	"2007-11-13T12:15:342+00:00",
	"2007-11-13T12:15:34+001:00",
	"2007-11-13T12:15:32+00:006",
	"2007-02-29T15:13:21Z"
};

unsigned int invalid_date_test_count = N_ELEMENTS(invalid_date_tests);

static void test_iso8601_date_invalid(void)
{
	unsigned int i;

	for (i = 0; i < invalid_date_test_count; i++) T_BEGIN {
		const char *date_in;
		struct tm tm;
		int tz;
		bool result;

		date_in = invalid_date_tests[i];

		test_begin(t_strdup_printf("iso8601 date invalid [%d]", i));

		result = iso8601_date_parse_tm
			((const unsigned char *)date_in, strlen(date_in), &tm, &tz);
		test_out(t_strdup_printf("parse %s", date_in), !result);

		test_end();
	} T_END;
}

void test_iso8601_date(void)
{
	test_iso8601_date_valid();
	test_iso8601_date_invalid();
}
