/*======================================================================
 FILE: regression.c
 CREATOR: eric 03 April 1999

 (C) COPYRIGHT 1999 Eric Busboom <eric@civicknowledge.com>

 This library is free software; you can redistribute it and/or modify
 it under the terms of either:

    The LGPL as published by the Free Software Foundation, version
    2.1, available at: https://www.gnu.org/licenses/lgpl-2.1.html

 Or:

    The Mozilla Public License Version 2.0. You may obtain a copy of
    the License at https://www.mozilla.org/MPL/

 The original author is Eric Busboom
 The original code is regression.c
======================================================================*/
//krazy:skip
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "regression.h"
#include "libical/ical.h"
#include "libicalss/icalss.h"
#include "libicalvcal/icalvcal.h"
#include "libicalvcal/vobject.h"
#include "libicalvcal/vcc.h"

#include <assert.h>
#include <stdlib.h>

#define TESTS_TZID_PREFIX "/softwarestudio.org/tests/"

/* For GNU libc, strcmp appears to be a macro, so using strcmp in
 assert results in incomprehansible assertion messages. This
 eliminates the problem */

int regrstrcmp(const char *a, const char *b)
{
    return strcmp(a, b);
}

/* This example creates and minipulates the ical object that appears
 * in rfc 2445, page 137 */
/*
static char str[] = "BEGIN:VCALENDAR\
PRODID:\"-//RDU Software//NONSGML HandCal//EN\"\
VERSION:2.0\
BEGIN:VTIMEZONE\
TZID:America/New_York\
BEGIN:STANDARD\
DTSTART:19981025T020000\
RDATE:19981025T020000\
TZOFFSETFROM:-0400\
TZOFFSETTO:-0500\
TZNAME:EST\
END:STANDARD\
BEGIN:DAYLIGHT\
DTSTART:19990404T020000\
RDATE:19990404T020000\
TZOFFSETFROM:-0500\
TZOFFSETTO:-0400\
TZNAME:EDT\
END:DAYLIGHT\
END:VTIMEZONE\
BEGIN:VEVENT\
DTSTAMP:19980309T231000Z\
UID:guid-1.host1.com\
ORGANIZER;ROLE=CHAIR:MAILTO:mrbig@host.com\
ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP:MAILTO:employee-A@host.com\
DESCRIPTION:Project XYZ Review Meeting\
CATEGORIES:MEETING\
CLASS:PUBLIC\
CREATED:19980309T130000Z\
SUMMARY:XYZ Project Review\
DTSTART;TZID=America/New_York:19980312T083000\
DTEND;TZID=America/New_York:19980312T093000\
LOCATION:1CP Conference Room 4350\
END:VEVENT\
BEGIN:BOOGA\
DTSTAMP:19980309T231000Z\
X-LIC-FOO:Booga\
DTSTOMP:19980309T231000Z\
UID:guid-1.host1.com\
END:BOOGA\
END:VCALENDAR";
*/

/* Returns a list of all attendees who are required. */
/*
static char** get_required_attendees(icalcomponent* event)
{
    icalproperty* p;
    icalparameter* parameter;

    char **attendees;
    int max = 10;
    int c = 0;

    attendees = malloc(max * (sizeof (char *)));

    ok("event is non null", (event != 0));
    int_is("event is a VEVENT", icalcomponent_isa(event), ICAL_VEVENT_COMPONENT);

    for(
        p = icalcomponent_get_first_property(event,ICAL_ATTENDEE_PROPERTY);
        p != 0;
        p = icalcomponent_get_next_property(event,ICAL_ATTENDEE_PROPERTY)
        ) {

        parameter = icalproperty_get_first_parameter(p,ICAL_ROLE_PARAMETER);

        if ( icalparameter_get_role(parameter) == ICAL_ROLE_REQPARTICIPANT)
        {
            attendees[c++] = icalmemory_strdup(icalproperty_get_attendee(p));

            if (c >= max) {
                max *= 2;
                attendees = realloc(attendees, max * (sizeof (char *)));
            }
        }
    }

    return attendees;
}

*/

/* If an attendee has a PARTSTAT of NEEDSACTION or has no PARTSTAT
   parameter, change it to TENTATIVE. */
 /*
    static void update_attendees(icalcomponent* event)
    {
    icalproperty* p;
    icalparameter* parameter;

    assert(event != 0);
    assert(icalcomponent_isa(event) == ICAL_VEVENT_COMPONENT);

    for(
    p = icalcomponent_get_first_property(event,ICAL_ATTENDEE_PROPERTY);
    p != 0;
    p = icalcomponent_get_next_property(event,ICAL_ATTENDEE_PROPERTY)
    ) {

    parameter = icalproperty_get_first_parameter(p,ICAL_PARTSTAT_PARAMETER);

    if (parameter == 0) {

    icalproperty_add_parameter(
    p,
    icalparameter_new_partstat(ICAL_PARTSTAT_TENTATIVE)
    );

    } else if (icalparameter_get_partstat(parameter) == ICAL_PARTSTAT_NEEDSACTION) {

    icalproperty_remove_parameter_by_ref(p,parameter);

    icalparameter_free(parameter);

    icalproperty_add_parameter(
    p,
    icalparameter_new_partstat(ICAL_PARTSTAT_TENTATIVE)
    );
    }
    }
    }
  */

void test_values(void)
{
    icalvalue *v;
    icalvalue *copy;

    v = icalvalue_new_caladdress("cap://value/1");

    str_is("icalvalue_new_caladdress()", icalvalue_get_caladdress(v), "cap://value/1");

    icalvalue_set_caladdress(v, "cap://value/2");

    str_is("icalvalue_set_caladdress()", icalvalue_get_caladdress(v), "cap://value/2");

    str_is("icalvalue_as_ical_string()", icalvalue_as_ical_string(v), "cap://value/2");

    copy = icalvalue_new_clone(v);

    str_is("icalvalue_new_clone()", icalvalue_as_ical_string(copy), "cap://value/2");
    icalvalue_free(v);
    icalvalue_free(copy);

    v = icalvalue_new_boolean(0);
    int_is("icalvalue_new_boolean(0)", icalvalue_get_boolean(v), 0);
    str_is("icalvalue_as_ical_string()", icalvalue_as_ical_string(v), "FALSE");
    icalvalue_set_boolean(v, 2);
    ok("icalvalue_set_boolean(2)", (2 == icalvalue_get_boolean(v)));
    str_is("icalvalue_as_ical_string()", icalvalue_as_ical_string(v), "TRUE");

    copy = icalvalue_new_clone(v);
    str_is("icalvalue_new_clone()", icalvalue_as_ical_string(copy), "TRUE");

    icalvalue_free(v);
    icalvalue_free(copy);

    v = icalvalue_new_x("test");
    str_is("icalvalue_new_x(test)", icalvalue_get_x(v), "test");
    icalvalue_set_x(v, "test2");
    str_is("icalvalue_set_x(test2)", icalvalue_get_x(v), "test2");
    str_is("icalvalue_as_ical_string()", icalvalue_as_ical_string(v), "test2");

    copy = icalvalue_new_clone(v);
    str_is("icalvalue_new_clone()", icalvalue_as_ical_string(copy), "test2");

    icalvalue_free(v);
    icalvalue_free(copy);

    v = icalvalue_new_datetime(icaltime_from_timet_with_zone(1023404802, 0, NULL));
    str_is("icalvalue_new_datetime()", icalvalue_as_ical_string(v), "20020606T230642");
    icalvalue_set_datetime(v, icaltime_from_timet_with_zone(1023404802 - 3600, 0, NULL));
    str_is("icalvalue_set_datetime()", icalvalue_as_ical_string(v), "20020606T220642");

    copy = icalvalue_new_clone(v);
    str_is("icalvalue_new_clone()", icalvalue_as_ical_string(v), "20020606T220642");

    icalvalue_free(v);
    icalvalue_free(copy);

    v = icalvalue_new(-1);

    ok("icalvalue_new(-1), Invalid type", (v == NULL));

    if (v != 0)
        icalvalue_free(v);

    ok("ICAL_BOOLEAN_VALUE", (ICAL_BOOLEAN_VALUE ==
                              icalparameter_value_to_value_kind(ICAL_VALUE_BOOLEAN)));
    ok("ICAL_UTCOFFSET_VALUE", (ICAL_UTCOFFSET_VALUE ==
                                icalparameter_value_to_value_kind(ICAL_VALUE_UTCOFFSET)));
    ok("ICAL_RECUR_VALUE", (ICAL_RECUR_VALUE ==
                            icalparameter_value_to_value_kind(ICAL_VALUE_RECUR)));
    ok("ICAL_CALADDRESS_VALUE", (ICAL_CALADDRESS_VALUE ==
                                 icalparameter_value_to_value_kind(ICAL_VALUE_CALADDRESS)));
    ok("ICAL_PERIOD_VALUE", (ICAL_PERIOD_VALUE ==
                             icalparameter_value_to_value_kind(ICAL_VALUE_PERIOD)));
    ok("ICAL_BINARY_VALUE", (ICAL_BINARY_VALUE ==
                             icalparameter_value_to_value_kind(ICAL_VALUE_BINARY)));
    ok("ICAL_TEXT_VALUE", (ICAL_TEXT_VALUE == icalparameter_value_to_value_kind(ICAL_VALUE_TEXT)));
    ok("ICAL_DURATION_VALUE", (ICAL_DURATION_VALUE ==
                               icalparameter_value_to_value_kind(ICAL_VALUE_DURATION)));
    ok("ICAL_INTEGER_VALUE",
       (ICAL_INTEGER_VALUE == icalparameter_value_to_value_kind(ICAL_VALUE_INTEGER)));

    ok("ICAL_URI_VALUE", (ICAL_URI_VALUE == icalparameter_value_to_value_kind(ICAL_VALUE_URI)));
    ok("ICAL_FLOAT_VALUE", (ICAL_FLOAT_VALUE ==
                            icalparameter_value_to_value_kind(ICAL_VALUE_FLOAT)));
    ok("ICAL_X_VALUE", (ICAL_X_VALUE == icalparameter_value_to_value_kind(ICAL_VALUE_X)));
    ok("ICAL_DATETIME_VALUE", (ICAL_DATETIME_VALUE ==
                               icalparameter_value_to_value_kind(ICAL_VALUE_DATETIME)));
    ok("ICAL_DATE_TIME", (ICAL_DATE_VALUE == icalparameter_value_to_value_kind(ICAL_VALUE_DATE)));

    /*    v = icalvalue_new_caladdress(0);

       printf("Bad string: %p\n",v);

       if (v!=0) icalvalue_free(v); */

    icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR, ICAL_ERROR_NONFATAL);
    v = icalvalue_new_from_string(ICAL_RECUR_VALUE, "D2 #0");
    ok("illegal recur value", (v == 0));

    v = icalvalue_new_from_string(ICAL_TRIGGER_VALUE, "Gonk");
    ok("illegal trigger value", (v == 0));

    v = icalvalue_new_from_string(ICAL_REQUESTSTATUS_VALUE, "Gonk");
    ok("illegal requeststatus value", (v == 0));

    icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR, ICAL_ERROR_DEFAULT);
}

void test_properties(void)
{
    icalproperty *prop;
    icalparameter *param;

    icalproperty *clone;
    char test_cn_str[128] = "";
    const char *test_cn_str_good = "A Common Name 1A Common Name 2A Common Name 3A Common Name 4";
    const char *test_ical_str_good =
        "COMMENT;CN=A Common Name 1;CN=A Common Name 2;CN=A Common Name 3;CN=A \r\n Common Name 4:Another Comment\r\n";

    prop = icalproperty_vanew_comment("Another Comment",
                                      icalparameter_new_cn("A Common Name 1"),
                                      icalparameter_new_cn("A Common Name 2"),
                                      icalparameter_new_cn("A Common Name 3"),
                                      icalparameter_new_cn("A Common Name 4"), (void *)0);

    for (param = icalproperty_get_first_parameter(prop, ICAL_ANY_PARAMETER);
         param != 0;
         param = icalproperty_get_next_parameter(prop, ICAL_ANY_PARAMETER)) {
        const char *str = icalparameter_get_cn(param);

        if (VERBOSE) {
            printf("Prop parameter: %s\n", icalparameter_get_cn(param));
        }
        strcat(test_cn_str, str);
    }
    str_is("fetching parameters", test_cn_str, test_cn_str_good);

    if (VERBOSE) {
        printf("Prop value: %s\n", icalproperty_get_comment(prop));
    }
    str_is("icalproperty_get_comment()", icalproperty_get_comment(prop), "Another Comment");

    if (VERBOSE) {
        printf("As iCAL string:\n %s\n", icalproperty_as_ical_string(prop));
    }
    str_is("icalproperty_as_ical_string()", icalproperty_as_ical_string(prop), test_ical_str_good);

    clone = icalproperty_new_clone(prop);

    if (VERBOSE) {
        printf("Clone:\n %s\n", icalproperty_as_ical_string(prop));
    }
    str_is("icalproperty_new_clone()", icalproperty_as_ical_string(prop), test_ical_str_good);

    icalproperty_free(clone);
    icalproperty_free(prop);

    prop = icalproperty_new(-1);

    ok("test icalproperty_new() with invalid type (-1)", (prop == NULL));

    if (prop != 0)
        icalproperty_free(prop);
}

void test_utf8(void)
{
    icalcomponent *comp;
    icalproperty *prop;
    const char *utf8text =
        "aáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaá";
    const char *test_ical_str_good =
        "DESCRIPTION:\r\n"
        " aáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaá\r\n"
        " óaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóaáóa\r\n"
        " áóaáóaáóaáóaáóaáóaáóaáóaáóaá\r\n";

    const char *item =
        "BEGIN:VCALENDAR\n"
        "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
        "VERSION:2.0\n"
        "METHOD:PUBLISH\n"
        "BEGIN:VEVENT\n"
        "SUMMARY:  áó aáóaáó aáä\n"
        "LOCATION:áóaáóaáóaä  áóaáóaáóaáóaáóaáóaáóaáóaáóaáó  aáóaáóaáóaáóaá \n"
        "END:VEVENT\n"
        "END:VCALENDAR\n";

    const char *agenda93 =
        "BEGIN:VCALENDAR\n"
        "VERSION:2.0\n"
        "CALSCALE:GREGORIAN\n"
        "PRODID:-//Henrik Levkowetz//ietf-agenda-ical 1.03//EN\n"
        "BEGIN:VEVENT\n"
        "DTSTART;TZID=Europe/Prague:20150721T190000\n"
        "DTEND;TZID=Europe/Prague:20150721T230000\n"
        "DTSTAMP:20150627T071730Z\n"
        "UID:ical-ietf-93-3ce43c64.1@tools.ietf.org\n"
        "SEQUENCE:197\n"
        "SUMMARY:IETF 93 Social Event at the Žofín Palace\n"
        "END:VEVENT\n"
        "END:VCALENDAR\n";

    const char *utf8_marker =
        "\xEF\xBB\xBF"
        "BEGIN:VCALENDAR\n"
        "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
        "VERSION:2.0\n"
        "METHOD:PUBLISH\n"
        "BEGIN:VEVENT\n"
        "SUMMARY:  áó aáóaáó aáä\n"
        "LOCATION:áóaáóaáóaä  áóaáóaáóaáóaáóaáóaáóaáóaáóaáó  aáóaáóaáóaáóaá \n"
        "END:VEVENT\n"
        "END:VCALENDAR\n";

    prop = icalproperty_new_description(utf8text);

    str_is("icalproperty_as_ical_string()", icalproperty_as_ical_string(prop), test_ical_str_good);
    icalproperty_free(prop);

    /* test issue 116 */
    comp = icalcomponent_new_from_string(item);
    ok("parsed", (comp != NULL));
    str_is("location", icalcomponent_get_location(comp), "áóaáóaáóaä  áóaáóaáóaáóaáóaáóaáóaáóaáóaáó  aáóaáóaáóaáóaá");
    str_is("summary", icalcomponent_get_summary(comp), "áó aáóaáó aáä");
    icalcomponent_free(comp);

    /* test another report against issue 116 */
    comp = icalcomponent_new_from_string(agenda93);
    ok("parsed", (comp != NULL));
    str_is("summary", icalcomponent_get_summary(comp), "IETF 93 Social Event at the Žofín Palace");
    icalcomponent_free(comp);

    /* test a string with UTF-8 marker at the beginning */
    comp = icalcomponent_new_from_string(utf8_marker);
    ok("parsed", (comp != NULL));
    ok("kind", (icalcomponent_isa(comp) == ICAL_VCALENDAR_COMPONENT));
    str_is("location", icalcomponent_get_location(comp), "áóaáóaáóaä  áóaáóaáóaáóaáóaáóaáóaáóaáóaáó  aáóaáóaáóaáóaá");
    str_is("summary", icalcomponent_get_summary(comp), "áó aáóaáó aáä");
    icalcomponent_free(comp);
}

void test_icaltime_compare_utc_zone(void)
{
    struct icaltimetype a;
    struct icaltimetype b;

    /* test comparing a UTC and floating time */
    a = icaltime_from_string("20170130T103000Z");
    b = icaltime_from_string("20170130T103000");
    int_is("a == b", icaltime_compare(a, b), 0);

    /* test comparing times across time zones (where UTC is specified by zone) */
    a = icaltime_from_string("20170130T103000Z");
    b = icaltime_from_string("20170130T103000");
    b.zone = icaltimezone_get_builtin_timezone("America/Los_Angeles");
    int_is("a < b", icaltime_compare(a, b), -1);
}

void test_parameters(void)
{
    icalparameter *p;
    int i;
    int enums[] =
        { ICAL_CUTYPE_INDIVIDUAL, ICAL_CUTYPE_RESOURCE, ICAL_FBTYPE_BUSY, ICAL_PARTSTAT_NEEDSACTION,
  ICAL_ROLE_NONPARTICIPANT, ICAL_XLICCOMPARETYPE_LESSEQUAL, ICAL_XLICERRORTYPE_MIMEPARSEERROR, -1 };

    const char *str1 = "A Common Name";

    p = icalparameter_new_cn(str1);

    str_is("icalparameter_new_cn()", icalparameter_get_cn(p), str1);
    str_is("icalparameter_as_ical_string()", icalparameter_as_ical_string(p), "CN=A Common Name");

    icalparameter_free(p);

    p = icalparameter_new_cn("");

    str_is("icalparameter_new_cn()", icalparameter_get_cn(p), "");
    str_is("icalparameter_as_ical_string()", icalparameter_as_ical_string(p), "CN=\"\"");

    icalparameter_free(p);

    p = icalparameter_new_from_string("PARTSTAT=ACCEPTED");
    ok("PARTSTAT_PARAMETER", (icalparameter_isa(p) == ICAL_PARTSTAT_PARAMETER));
    ok("PARTSTAT_ACCEPTED", (icalparameter_get_partstat(p) == ICAL_PARTSTAT_ACCEPTED));

    icalparameter_free(p);

    p = icalparameter_new_from_string("ROLE=CHAIR");

    ok("ROLE_PARAMETER", (icalparameter_isa(p) == ICAL_ROLE_PARAMETER));
    ok("ROLE_CHAIR", (icalparameter_get_role(p) == ICAL_ROLE_CHAIR));

    icalparameter_free(p);

    p = icalparameter_new_from_string("PARTSTAT=X-FOO");
    ok("PARTSTAT_PARAMETER", (icalparameter_isa(p) == ICAL_PARTSTAT_PARAMETER));
    ok("PARTSTAT_X", (icalparameter_get_partstat(p) == ICAL_PARTSTAT_X));

    icalparameter_free(p);

    p = icalparameter_new_from_string("X-PARAM=X-FOO");
    ok("X_PARAMETER", (icalparameter_isa(p) == ICAL_X_PARAMETER));

    icalparameter_free(p);

    for (i = 0; enums[i] != -1; i++) {
        if (VERBOSE) {
            printf("%s\n", icalparameter_enum_to_string(enums[i]));
        }
        ok("test parameter enums",
           (icalparameter_string_to_enum(icalparameter_enum_to_string(enums[i])) == enums[i]));
    }
}

const char *good_child =
    "BEGIN:VEVENT\r\n"
    "VERSION:2.0\r\n"
    "DESCRIPTION:This is an event\r\n"
    "COMMENT;CN=A Common Name 1;CN=A Common Name 2;CN=A Common Name 3;CN=A \r\n"
    " Common Name 4:Another Comment\r\n"
    "X-LIC-ERROR;X-LIC-ERRORTYPE=COMPONENT-PARSE-ERROR:This is only a test\r\n" "END:VEVENT\r\n";

const char *bad_child =
    "BEGIN;\r\n"
    "TZNAME:\\r\n"
    "TZNAME:\\r\n"
    "TZNAME:\\r\n"
    "TZNAME:\\r\n"
    "TZNAME:\\r\n"
    "TZNAME:\\r\n"
    "TZNAME:\\r\n";

void test_components(void)
{
    icalcomponent *c;
    icalcomponent *child;
    int estate;

    c = icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
            icalproperty_new_version("2.0"),
            icalproperty_new_prodid("-//RDU Software//NONSGML HandCal//EN"),
            icalproperty_vanew_comment("A Comment",
                icalparameter_new_cn("A Common Name 1"), (void *)0),
            icalcomponent_vanew(ICAL_VEVENT_COMPONENT,
                                icalproperty_new_version("2.0"),
                                icalproperty_new_description("This is an event"),
                                icalproperty_vanew_comment(
                                    "Another Comment",
                                    icalparameter_new_cn("A Common Name 1"),
                                    icalparameter_new_cn("A Common Name 2"),
                                    icalparameter_new_cn("A Common Name 3"),
                                    icalparameter_new_cn("A Common Name 4"),
                                    (void *)0),
                                icalproperty_vanew_xlicerror(
                                    "This is only a test",
                                    icalparameter_new_xlicerrortype(ICAL_XLICERRORTYPE_COMPONENTPARSEERROR),
                                    (void *)0),
                                (void *)0),
            (void *)0);

    if (VERBOSE)
        printf("Original Component:\n%s\n\n", icalcomponent_as_ical_string(c));

    child = icalcomponent_get_first_component(c, ICAL_VEVENT_COMPONENT);

    ok("test icalcomponent_get_first_component()", (child != NULL));

    if (VERBOSE)
        printf("Child Component:\n%s\n\n", icalcomponent_as_ical_string(child));

    str_is("test results of child component", icalcomponent_as_ical_string(child), good_child);
    icalcomponent_free(c);

    estate = icalerror_get_errors_are_fatal();
    icalerror_set_errors_are_fatal(0);
    c = icalcomponent_new_from_string(bad_child);
    ok("parse failed as expected", (c == NULL));
    icalcomponent_free(c);
    icalerror_set_errors_are_fatal(estate);
}

static void test_component_foreach_callback(icalcomponent *comp, struct icaltime_span *span, void *data)
{
    int *foundExpected;
    _unused(comp);
    _unused(span);

    foundExpected = (int*)data;
    (*foundExpected)++;
}

int test_component_foreach_parameterized(int startOffsSec, int endOffsSec, int expectedFoundInstances)
{
    const char *calStr =
        "BEGIN:VCALENDAR\n"
        "BEGIN:VEVENT\n"
        "DTSTART;20180220T020000Z\n"
        "DURATION:PT1H\n"
        "RRULE:FREQ=WEEKLY;BYDAY=TU\n"
        "END:VEVENT\n"
        "END:VCALENDAR\n";

    icalcomponent *calendar = icalparser_parse_string(calStr);
    icalcomponent *event = icalcomponent_get_first_component(calendar, ICAL_VEVENT_COMPONENT);

    struct icaltimetype dtstart = icaltime_from_string("20180220T020000Z");
    struct icaltimetype it_start = dtstart;
    struct icaltimetype it_end = dtstart;
    int foundCnt = 0;

    icaltime_adjust(&it_start, 0, 0, 0, startOffsSec);
    icaltime_adjust(&it_end, 0, 0, 0, endOffsSec);

    icalcomponent_foreach_recurrence(event, it_start, it_end, test_component_foreach_callback, &foundCnt);
    icalcomponent_free(calendar);

    ok("icalcomponent_foreach_recurrence yielded the expected number of instances.", foundCnt == expectedFoundInstances);

    return 0;
}

void test_component_foreach(void)
{
    const char *calStr =
        "BEGIN:VCALENDAR\n"
        "BEGIN:VEVENT\n"
        /* 14th is NOT a tuesday, so it doesn't conform to the rule. */
        "DTSTART;20180214T020000Z\n"
        "DURATION:PT4H\n"
        "RRULE:FREQ=WEEKLY;COUNT=2;BYDAY=TU\n"
        "END:VEVENT\n"
        "END:VCALENDAR\n";

    icalcomponent *calendar = icalparser_parse_string(calStr);
    icalcomponent *event = icalcomponent_get_first_component(calendar, ICAL_VEVENT_COMPONENT);

    struct icaltimetype t_start = icaltime_from_string("20180219T000000Z");
    struct icaltimetype t_end = icaltime_from_string("20180221T000000Z");

    int foundExpectedCnt = 0;
    int i;

    /* we expect exactly one callback, namely on tu, 20th. */
    icalcomponent_foreach_recurrence(event, t_start, t_end, test_component_foreach_callback, &foundExpectedCnt);

    icalcomponent_free(calendar);

    ok("Exactly one instance was returned as expected in a VEVENT where DTSTART doesn't conform to the contained RRULE.", foundExpectedCnt == 1);

    calStr =
        "BEGIN:VCALENDAR\n"
        "BEGIN:VEVENT\n"
        /* Now DTSTART conforms to the RRULE. */
        "DTSTART;20180220T020000Z\n"
        "DURATION:PT4H\n"
        "RRULE:FREQ=WEEKLY;COUNT=2;BYDAY=TU\n"
        "END:VEVENT\n"
        "END:VCALENDAR\n";

    calendar = icalparser_parse_string(calStr);
    event = icalcomponent_get_first_component(calendar, ICAL_VEVENT_COMPONENT);

    t_start = icaltime_from_string("20180219T000000Z");
    t_end = icaltime_from_string("20180221T000000Z");

    foundExpectedCnt = 0;
    /* we expect exactly one callback, namely on tu, 20th. */
    icalcomponent_foreach_recurrence(event, t_start, t_end, test_component_foreach_callback, &foundExpectedCnt);

    icalcomponent_free(calendar);

    ok("Exactly one instance was returned as expected in a VEVENT where DTSTART *does* conform to the contained RRULE.", foundExpectedCnt == 1);

    /* No RRULE in this VEVENT */
    calStr =
        "BEGIN:VCALENDAR\n"
        "BEGIN:VEVENT\n"
        /* Now DTSTART conforms to the RRULE. */
        "DTSTART;20180220T020000Z\n"
        "DURATION:PT4H\n"
        "END:VEVENT\n"
        "END:VCALENDAR\n";

    calendar = icalparser_parse_string(calStr);
    event = icalcomponent_get_first_component(calendar, ICAL_VEVENT_COMPONENT);

    t_start = icaltime_from_string("20180219T000000Z");
    t_end = icaltime_from_string("20180221T000000Z");

    foundExpectedCnt = 0;
    icalcomponent_foreach_recurrence(event, t_start, t_end, test_component_foreach_callback, &foundExpectedCnt);
    ok("Exactly one instance was returned for an event without a RRULE.", foundExpectedCnt == 1);

    t_start = icaltime_from_string("20180221T000000Z");
    t_end = icaltime_from_string("20180223T000000Z");

    foundExpectedCnt = 0;
    icalcomponent_foreach_recurrence(event, t_start, t_end, test_component_foreach_callback, &foundExpectedCnt);
    ok("No instance was returned for an event without a RRULE where DTSTART lies outside the iterator limits.", foundExpectedCnt == 0);

    icalcomponent_free(calendar);

    for (i = 0; i < 3; i++) {

        /* Add one week with every run, so the first run will address the
        first recurrence instance, the second run the second instance,
        etc.
        */
        int offs = i * 7 * 24 * 3600;

        test_component_foreach_parameterized(offs + -1, offs + -1, 0);
        test_component_foreach_parameterized(offs + -1, offs + 0, 0);
        test_component_foreach_parameterized(offs + -1, offs + 1, 1);
        test_component_foreach_parameterized(offs + 0, offs + 0, 0);
        test_component_foreach_parameterized(offs + 0, offs + 1, 1);
        test_component_foreach_parameterized(offs + 1, offs + 1, 1);
        test_component_foreach_parameterized(offs + 0, offs + 3600, 1);
        test_component_foreach_parameterized(offs + 3600 - 1, offs + 3600, 1);
        test_component_foreach_parameterized(offs + 3600 - 1, offs + 3600 + 1, 1);
        test_component_foreach_parameterized(offs + 3600, offs + 3600, 0);
        test_component_foreach_parameterized(offs + 3600, offs + 3600 + 1, 0);
        test_component_foreach_parameterized(offs + 3600 + 1, offs + 3600 + 1, 0);
    }
}

void test_recur_iterator_set_start(void)
{
    icaltimetype start = icaltime_from_string("20150526");
    struct icalrecurrencetype recurrence = icalrecurrencetype_from_string("FREQ=WEEKLY");
    icalrecur_iterator *iterator = icalrecur_iterator_new(recurrence, start);

    icaltimetype next = icalrecur_iterator_next(iterator);

    ok("Next recurrence iterator result should be of type date", next.is_date == 1);
    icalrecur_iterator_free(iterator);
}

void test_recur_iterator_on_jan_1(void)
{
    icaltimetype start = icaltime_from_string("20190101");
    struct icalrecurrencetype recurrence = icalrecurrencetype_from_string("FREQ=WEEKLY;WKST=SU;INTERVAL=2;BYDAY=MO,TU,WE,TH,FR");
    icalrecur_iterator *iterator = icalrecur_iterator_new(recurrence, start);

    icaltimetype next = icalrecur_iterator_next(iterator);
    ok("Next recurrence iterator result should be January 1", next.year == 2019 && next.month == 1 && next.day == 1);

    next = icalrecur_iterator_next(iterator);
    ok("Next recurrence iterator result should be January 2", next.year == 2019 && next.month == 1 && next.day == 2);
    icalrecur_iterator_free(iterator);
}

void test_memory(void)
{
    size_t bufsize = 256;
    int i;
    char *p;

    char S1[] = "1) When in the Course of human events, ";
    char S2[] = "2) it becomes necessary for one people to dissolve the political bands which have connected them with another, ";
    char S3[] = "3) and to assume among the powers of the earth, ";
    char S4[] = "4) the separate and equal station to which the Laws of Nature and of Nature's God entitle them, ";
    char S5[] = "5) a decent respect to the opinions of mankind requires that they ";
    char S6[] = "6) should declare the causes which impel them to the separation. ";
    char S7[] = "7) We hold these truths to be self-evident, ";
    char S8[] = "8) that all men are created equal, ";

/*    char S9[] = "9) that they are endowed by their Creator with certain unalienable Rights, ";
    char S10[] = "10) that among these are Life, Liberty, and the pursuit of Happiness. ";
    char S11[] = "11) That to secure these rights, Governments are instituted among Men, ";
    char S12[] = "12) deriving their just powers from the consent of the governed. ";
*/

    char *f, *b1, *b2, *b3, *b4, *b5, *b6, *b7, *b8;

#define BUFSIZE 1024

    f = icalmemory_new_buffer(bufsize);
    p = f;
    b1 = icalmemory_tmp_buffer(BUFSIZE);
    strcpy(b1, S1);
    icalmemory_append_string(&f, &p, &bufsize, b1);

    b2 = icalmemory_tmp_buffer(BUFSIZE);
    strcpy(b2, S2);
    icalmemory_append_string(&f, &p, &bufsize, b2);

    b3 = icalmemory_tmp_buffer(BUFSIZE);
    strcpy(b3, S3);
    icalmemory_append_string(&f, &p, &bufsize, b3);

    b4 = icalmemory_tmp_buffer(BUFSIZE);
    strcpy(b4, S4);
    icalmemory_append_string(&f, &p, &bufsize, b4);

    b5 = icalmemory_tmp_buffer(BUFSIZE);
    strcpy(b5, S5);
    icalmemory_append_string(&f, &p, &bufsize, b5);

    b6 = icalmemory_tmp_buffer(BUFSIZE);
    strcpy(b6, S6);
    icalmemory_append_string(&f, &p, &bufsize, b6);

    b7 = icalmemory_tmp_buffer(BUFSIZE);
    strcpy(b7, S7);
    icalmemory_append_string(&f, &p, &bufsize, b7);

    b8 = icalmemory_tmp_buffer(BUFSIZE);
    strcpy(b8, S8);
    icalmemory_append_string(&f, &p, &bufsize, b8);

    if (VERBOSE) {
        printf("1: %p %s \n", b1, b1);
        printf("2: %p %s\n", b2, b2);
        printf("3: %p %s\n", b3, b3);
        printf("4: %p %s\n", b4, b4);
        printf("5: %p %s\n", b5, b5);
        printf("6: %p %s\n", b6, b6);
        printf("7: %p %s\n", b7, b7);
        printf("8: %p %s\n", b8, b8);

        printf("Final: %s\n", f);
        printf("Final buffer size: %lu\n", (unsigned long)bufsize);
    }

    ok("final buffer size == 806", (bufsize == 806));

    free(f);

    bufsize = 4;

    f = icalmemory_new_buffer(bufsize);

    memset(f, 0, bufsize);
    p = f;

    icalmemory_append_char(&f, &p, &bufsize, 'a');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'b');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'c');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'd');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'e');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'f');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'g');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'h');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'i');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'j');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'a');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'b');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'c');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'd');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'e');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'f');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'g');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'h');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'i');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    icalmemory_append_char(&f, &p, &bufsize, 'j');
    if (VERBOSE)
        printf("Char-by-Char buffer: %s\n", f);

    free(f);

    for (i = 0; i < 100; i++) {
        f = icalmemory_tmp_buffer(bufsize);

        assert(f != 0);

        memset(f, 0, bufsize);
        snprintf(f, bufsize, "%d", i);
    }
}

void test_dirset(void)
{
    icalcomponent *c;
    icalgauge *gauge;
    icalerrorenum error;
    icalcomponent *next, *itr;
    icalset *cluster;
    icalset *s, *s2;
    struct icalperiodtype rtime;
    int i;
    int count = 0;

    (void)mkdir("store", 0755);
    (void)mkdir("store-new", 0755);

    s = icaldirset_new("store");
    s2 = icaldirset_new("store-new");

    ok("opening 'store' dirset", s != NULL);
    ok("opening 'store-new' dirset", s2 != NULL);

    rtime.start = icaltime_from_timet_with_zone(time(0), 0, NULL);

    cluster = icalfileset_new("clusterin.vcd");

    if (cluster == 0) {
        printf("Failed to create cluster: %s\n", icalerror_strerror(icalerrno));
    }
#define NUMCOMP 4

    /* Duplicate every component in the cluster NUMCOMP times */

    icalerror_clear_errno();

    for (i = 1; i < NUMCOMP + 1; i++) {

        /*rtime.start.month = i%12; */
        rtime.start.month = i;
        rtime.end = rtime.start;
        rtime.end.hour++;

        for (itr = icalfileset_get_first_component(cluster);
             itr != 0; itr = icalfileset_get_next_component(cluster)) {
            icalcomponent *clone;
            icalproperty *p;

            if (icalcomponent_isa(itr) != ICAL_VEVENT_COMPONENT) {
                continue;
            }

            /* Change the dtstart and dtend times in the component
               pointed to by Itr */

            clone = icalcomponent_new_clone(itr);
            assert(icalerrno == ICAL_NO_ERROR);
            assert(clone != 0);

            /* DTSTART */
            p = icalcomponent_get_first_property(clone, ICAL_DTSTART_PROPERTY);
            assert(icalerrno == ICAL_NO_ERROR);

            if (p == 0) {
                p = icalproperty_new_dtstart(rtime.start);
                icalcomponent_add_property(clone, p);
            } else {
                icalproperty_set_dtstart(p, rtime.start);
            }
            assert(icalerrno == ICAL_NO_ERROR);

            /* DTEND */
            p = icalcomponent_get_first_property(clone, ICAL_DTEND_PROPERTY);
            assert(icalerrno == ICAL_NO_ERROR);

            if (p == 0) {
                p = icalproperty_new_dtstart(rtime.end);
                icalcomponent_add_property(clone, p);
            } else {
                icalproperty_set_dtstart(p, rtime.end);
            }
            assert(icalerrno == ICAL_NO_ERROR);

            printf("\n----------\n%s\n---------\n", icalcomponent_as_ical_string(clone));
            error = icaldirset_add_component(s, clone);

            assert(error == ICAL_NO_ERROR);
            _unused(error);
        }
    }

    gauge = icalgauge_new_from_sql("SELECT * FROM VEVENT WHERE "
                   "VEVENT.SUMMARY = 'Submit Income Taxes' OR "
                   "VEVENT.SUMMARY = 'Bastille Day Party'",
                   0);

    (void)icaldirset_select(s, gauge);

    for (c = icaldirset_get_first_component(s); c != 0; c = icaldirset_get_next_component(s)) {
        printf("Got one! (%d)\n", count++);

        printf("%s", icalcomponent_as_ical_string(c));
        if (icaldirset_add_component(s2, c) == 0) {
            printf("Failed to write!\n");
        }
        icalcomponent_free(c);
    }

    icalset_free(s2);

    for (c = icaldirset_get_first_component(s); c != 0; c = next) {

        next = icaldirset_get_next_component(s);

        /*icaldirset_remove_component(s,c); */
        printf("%s", icalcomponent_as_ical_string(c));
    }

    icalset_free(s);
    icalset_free(cluster);
}

void test_compare(void)
{
    icalvalue *v1, *v2;

    v1 = icalvalue_new_caladdress("cap://value/1");
    v2 = icalvalue_new_clone(v1);

    ok("compare value and clone", (icalvalue_compare(v1, v2) == ICAL_XLICCOMPARETYPE_EQUAL));

    icalvalue_free(v1);
    icalvalue_free(v2);

    v1 = icalvalue_new_caladdress("A");
    v2 = icalvalue_new_caladdress("B");

    ok("test compare of A and B results in LESS",
       (icalvalue_compare(v1, v2) == ICAL_XLICCOMPARETYPE_LESS));

    ok("test compare of B and A results in GREATER",
       (icalvalue_compare(v2, v1) == ICAL_XLICCOMPARETYPE_GREATER));

    icalvalue_free(v1);
    icalvalue_free(v2);

    v1 = icalvalue_new_caladdress("B");
    v2 = icalvalue_new_caladdress("A");

    ok("test compare of caladdress A and B results in GREATER",
       (icalvalue_compare(v1, v2) == ICAL_XLICCOMPARETYPE_GREATER));

    icalvalue_free(v1);
    icalvalue_free(v2);

    v1 = icalvalue_new_integer(5);
    v2 = icalvalue_new_integer(5);

    ok("test compare of 5 and 5 results in EQUAL",
       (icalvalue_compare(v1, v2) == ICAL_XLICCOMPARETYPE_EQUAL));

    icalvalue_free(v1);
    icalvalue_free(v2);

    v1 = icalvalue_new_integer(5);
    v2 = icalvalue_new_integer(10);

    ok("test compare of 5 and 10 results in LESS",
       (icalvalue_compare(v1, v2) == ICAL_XLICCOMPARETYPE_LESS));

    ok("test compare of 10 and 5 results in GREATER",
       (icalvalue_compare(v2, v1) == ICAL_XLICCOMPARETYPE_GREATER));

    icalvalue_free(v1);
    icalvalue_free(v2);
}

void test_restriction(void)
{
    icalcomponent *comp;
    struct icaltimetype atime = icaltime_from_timet_with_zone(time(0), 0, NULL);
    int valid;

    struct icaldatetimeperiodtype rtime;

    rtime.period.start = icaltime_from_timet_with_zone(time(0), 0, NULL);
    rtime.period.end = icaltime_from_timet_with_zone(time(0), 0, NULL);
    rtime.period.end.hour++;
    rtime.period.duration = icaldurationtype_null_duration();
    rtime.time = icaltime_null_time();

    comp =
        icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
            icalproperty_new_version("2.0"),
            icalproperty_new_prodid("-//RDU Software//NONSGML HandCal//EN"),
            icalproperty_new_method(ICAL_METHOD_REQUEST),
            icalcomponent_vanew(
                ICAL_VTIMEZONE_COMPONENT,
                icalproperty_new_tzid("America/New_York"),
                icalcomponent_vanew(ICAL_XDAYLIGHT_COMPONENT,
                                    icalproperty_new_dtstart(atime),
                                    icalproperty_new_rdate(rtime),
                                    icalproperty_new_tzoffsetfrom(-4),
                                    icalproperty_new_tzoffsetto(-5),
                                    icalproperty_new_tzname("EST"),
                                    (void *)0),
                icalcomponent_vanew(ICAL_XSTANDARD_COMPONENT,
                                    icalproperty_new_dtstart(atime),
                                    icalproperty_new_rdate(rtime),
                                    icalproperty_new_tzoffsetfrom(-5),
                                    icalproperty_new_tzoffsetto(-4),
                                    icalproperty_new_tzname("EST"),
                                    (void *)0),
                (void *)0),
            icalcomponent_vanew(
                ICAL_VEVENT_COMPONENT,
                icalproperty_new_dtstamp(atime),
                icalproperty_new_uid("guid-1.host1.com"),
                icalproperty_vanew_organizer("mrbig@host.com",
                                             icalparameter_new_role(ICAL_ROLE_CHAIR),
                                             (void *)0),
                icalproperty_vanew_attendee("employee-A@host.com",
                                            icalparameter_new_role(ICAL_ROLE_REQPARTICIPANT),
                                            icalparameter_new_rsvp(ICAL_RSVP_TRUE),
                                            icalparameter_new_cutype(ICAL_CUTYPE_GROUP),
                                            (void *)0),
                icalproperty_new_description("Project XYZ Review Meeting"),
                icalproperty_new_categories("MEETING"),
                icalproperty_new_class(ICAL_CLASS_PUBLIC),
                icalproperty_new_created(atime),
                icalproperty_new_summary("XYZ Project Review"),
                /*              icalproperty_new_dtstart(
                                atime,
                                icalparameter_new_tzid("America/New_York"),
                                0
                                ), */
                icalproperty_vanew_dtend(atime,
                                         icalparameter_new_tzid("America/New_York"),
                                         (void *)0),
                icalproperty_new_location("1CP Conference Room 4350"), (void *)0), (void *)0);

    valid = icalrestriction_check(comp);
    (void)icalcomponent_as_ical_string(comp);
    icalcomponent_free(comp);
    ok("icalrestriction_check() == 0", (valid == 0));
}

void test_calendar(void)
{
    icalcomponent *comp;
    icalset *c;
    icalset *s;
    icalcalendar *calendar;
    icalerrorenum error;
    struct icaltimetype atime = icaltime_from_timet_with_zone(time(0), 0, NULL);

    (void)mkdir("calendar", 0755);
    (void)mkdir("calendar/booked", 0755);

    calendar = icalcalendar_new("calendar");

    comp =
        icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
            icalcomponent_vanew(
                ICAL_VEVENT_COMPONENT,
                icalproperty_new_version("2.0"),
                icalproperty_new_description("This is an event"),
                icalproperty_new_dtstart(atime),
                icalproperty_vanew_comment(
                    "Another Comment",
                    icalparameter_new_cn("A Common Name 1"),
                    icalparameter_new_cn("A Common Name 2"),
                    icalparameter_new_cn("A Common Name 3"),
                    icalparameter_new_cn("A Common Name 4"),
                    (void *)0),
                icalproperty_vanew_xlicerror(
                    "This is only a test",
                    icalparameter_new_xlicerrortype(ICAL_XLICERRORTYPE_COMPONENTPARSEERROR),
                    (void *)0),
                (void *)0),
            (void *)0);

    s = icalcalendar_get_booked(calendar);

    error = icaldirset_add_component(s, comp);

    ok("Adding Component to dirset", (error == ICAL_NO_ERROR));

    c = icalcalendar_get_properties(calendar);

    error = icalfileset_add_component(c, icalcomponent_new_clone(comp));

    ok("Adding Clone Component to dirset", (error == ICAL_NO_ERROR));

    icalcalendar_free(calendar);

    ok("icalcalendar test", (1));
}

void test_increment(void);

/* coverity[pass_by_value] */
void print_occur(struct icalrecurrencetype recur, struct icaltimetype start)
{
    struct icaltimetype next;
    icalrecur_iterator *ritr;

    time_t tt = icaltime_as_timet(start);

    printf("#### %s\n", icalrecurrencetype_as_string(&recur));
    printf("#### %s\n", ctime(&tt));

    ritr = icalrecur_iterator_new(recur, start);
    for (next = icalrecur_iterator_next(ritr);
         !icaltime_is_null_time(next);
         next = icalrecur_iterator_next(ritr)) {
        tt = icaltime_as_timet(next);
        printf("  %s", ctime(&tt));
    }

    icalrecur_iterator_free(ritr);
}

void test_recur(void)
{
    struct icalrecurrencetype rt;
    struct icaltimetype start;
    time_t array[25];
    int i;

    rt = icalrecurrencetype_from_string(
             "FREQ=MONTHLY;UNTIL=19971224T000000Z;INTERVAL=1;BYDAY=TU,2FR,3SA");
    start = icaltime_from_string("19970905T090000Z");

    if (VERBOSE) {
        print_occur(rt, start);
    }

    if (VERBOSE) {
        printf("\n  Using icalrecur_expand_recurrence\n");
    }

    icalrecur_expand_recurrence(
        "FREQ=MONTHLY;UNTIL=19971224T000000Z;INTERVAL=1;BYDAY=TU,2FR,3SA",
        icaltime_as_timet(start), 25, array);

    for (i = 0; i < 25 && array[i] != 0; i++) {
        if (VERBOSE) {
            printf("  %s", ctime(&(array[i])));
        }
    }
/*    test_increment();*/
}

void test_expand_recurrence(void)
{
    time_t arr[10];
    time_t now = 931057385;
    int i, numfound = 0;

    icalrecur_expand_recurrence("FREQ=MONTHLY;BYDAY=MO,WE", now, 5, arr);

    if (VERBOSE)
        printf("Start %s", ctime(&now));

    for (i = 0; i < 5; i++) {
        numfound++;
        if (VERBOSE)
            printf("i=%d  %s\n", i, ctime(&arr[i]));
    }
    int_is("Get an array of 5 items", numfound, 5);
}

enum byrule
{
    NO_CONTRACTION = -1,
    BY_SECOND = 0,
    BY_MINUTE = 1,
    BY_HOUR = 2,
    BY_DAY = 3,
    BY_MONTH_DAY = 4,
    BY_YEAR_DAY = 5,
    BY_WEEK_NO = 6,
    BY_MONTH = 7,
    BY_SET_POS
};

void icalrecurrencetype_test(void)
{
    icalvalue *v =
        icalvalue_new_from_string(
            ICAL_RECUR_VALUE,
            "FREQ=YEARLY;UNTIL=20060101T000000;INTERVAL=2;BYDAY=SU,WE;BYSECOND=15,30; BYMONTH=1,6,11");

    struct icalrecurrencetype r = icalvalue_get_recur(v);
    struct icaltimetype t = icaltime_from_timet_with_zone(time(0), 0, NULL);
    struct icaltimetype next;
    time_t tt;

    struct icalrecur_iterator_impl *itr =
        (struct icalrecur_iterator_impl *)icalrecur_iterator_new(r, t);

    do {

        next = icalrecur_iterator_next(itr);
        tt = icaltime_as_timet(next);

        printf("%s", ctime(&tt));

    } while (!icaltime_is_null_time(next));

    icalvalue_free(v);

    icalrecur_iterator_free(itr);
}

/* From Federico Mena Quintero <federico@ximian.com>    */
void test_recur_parameter_bug(void)
{
    static const char test_icalcomp_str[] =
        "BEGIN:VEVENT\r\n"
        "RRULE;X-EVOLUTION-ENDDATE=20030209T081500:FREQ=DAILY;COUNT=10;INTERVAL=6\r\n"
        "END:VEVENT\r\n";

    icalcomponent *icalcomp;
    icalproperty *prop;
    struct icalrecurrencetype recur;
    int n_errors;
    char *str;

    icalcomp = icalparser_parse_string((char *)test_icalcomp_str);
    ok("icalparser_parse_string()", (icalcomp != NULL));
    assert(icalcomp != NULL);

    str = icalcomponent_as_ical_string(icalcomp);
    str_is("parsed matches original", str, (char *)test_icalcomp_str);
    if (VERBOSE)
        printf("%s\n\n", str);

    n_errors = icalcomponent_count_errors(icalcomp);
    int_is("no parse errors", n_errors, 0);

    if (n_errors) {
        icalproperty *p;
        for (p = icalcomponent_get_first_property(icalcomp, ICAL_XLICERROR_PROPERTY);
             p;
             p = icalcomponent_get_next_property(icalcomp, ICAL_XLICERROR_PROPERTY)) {
            const char *str;
            str = icalproperty_as_ical_string(p);
            fprintf(stderr, "error: %s\n", str);
        }
    }

    prop = icalcomponent_get_first_property(icalcomp, ICAL_RRULE_PROPERTY);
    ok("get RRULE property", (prop != NULL));
    assert(prop != NULL);

    recur = icalproperty_get_rrule(prop);

    if (VERBOSE)
        printf("%s\n", icalrecurrencetype_as_string(&recur));

    icalcomponent_free(icalcomp);
}

void test_duration(void)
{
    struct icaldurationtype d;

    d = icaldurationtype_from_string("PT8H30M");
    if (VERBOSE) {
        printf("%s\n", icaldurationtype_as_ical_string(d));
    }
    int_is("PT8H30M", icaldurationtype_as_int(d), 30600);

    d = icaldurationtype_from_string("-PT8H30M");
    if (VERBOSE) {
        printf("%s\n", icaldurationtype_as_ical_string(d));
    }
    int_is("-PT8H30M", icaldurationtype_as_int(d), -30600);

    d = icaldurationtype_from_string("PT10H10M10S");
    if (VERBOSE) {
        printf("%s\n", icaldurationtype_as_ical_string(d));
    }
    int_is("PT10H10M10S", icaldurationtype_as_int(d), 36610);

    d = icaldurationtype_from_string("P7W");
    if (VERBOSE) {
        printf("%s\n", icaldurationtype_as_ical_string(d));
    }
    int_is("P7W", icaldurationtype_as_int(d), 4233600);

    d = icaldurationtype_from_string("P2DT8H30M");
    if (VERBOSE) {
        printf("%s\n", icaldurationtype_as_ical_string(d));
    }
    int_is("P2DT8H30M", icaldurationtype_as_int(d), 203400);

    d = icaldurationtype_from_string("P2W1DT5H");
    if (VERBOSE) {
        printf("%s %d\n", icaldurationtype_as_ical_string(d), icaldurationtype_as_int(d));
    }
    int_is("P2W1DT5H", icaldurationtype_as_int(d), 1314000);

    icalerror_set_errors_are_fatal(0);

    /* Test conversion of bad input */

    d = icaldurationtype_from_int(1314000);
    if (VERBOSE) {
        printf("%s %d\n", icaldurationtype_as_ical_string(d), icaldurationtype_as_int(d));
    }
    str_is("1314000", icaldurationtype_as_ical_string(d), "P15DT5H");

    d = icaldurationtype_from_string("P-2DT8H30M");
    if (VERBOSE) {
        printf("%s\n", icaldurationtype_as_ical_string(d));
    }
    int_is("P-2DT8H30M", icaldurationtype_as_int(d), 0);

    d = icaldurationtype_from_string("P7W8H");
    if (VERBOSE) {
        printf("%s\n", icaldurationtype_as_ical_string(d));
    }
    int_is("P7W8H", icaldurationtype_as_int(d), 0);

    d = icaldurationtype_from_string("T10H");
    if (VERBOSE) {
        printf("%s\n", icaldurationtype_as_ical_string(d));
    }
    int_is("T10H", icaldurationtype_as_int(d), 0);

    icalerror_set_errors_are_fatal(1);

    d = icaldurationtype_from_int(4233600);
    if (VERBOSE) {
        printf("%s\n", icaldurationtype_as_ical_string(d));
    }
    str_is("P7W", icaldurationtype_as_ical_string(d), "P7W");

    d = icaldurationtype_from_int(4424400);
    if (VERBOSE) {
        printf("%s\n", icaldurationtype_as_ical_string(d));
    }
    str_is("P51DT5H", icaldurationtype_as_ical_string(d), "P51DT5H");
}

void test_period(void)
{
    struct icalperiodtype p;
    icalvalue *v;
    const char *str;

    str = "19971015T050000Z/PT8H30M";
    p = icalperiodtype_from_string(str);
    str_is(str, icalperiodtype_as_ical_string(p), str);

    str = "19971015T050000Z/19971015T060000Z";
    p = icalperiodtype_from_string(str);
    str_is(str, icalperiodtype_as_ical_string(p), str);

    str = "19970101T120000/PT3H";

    v = icalvalue_new_from_string(ICAL_PERIOD_VALUE, str);
    str_is(str, icalvalue_as_ical_string(v), str);

    icalvalue_free(v);
}

void test_strings(void)
{
    icalvalue *v;

    v = icalvalue_new_text("foo;bar;bats");
    if (VERBOSE)
        printf("%s\n", icalvalue_as_ical_string(v));

    str_is("test encoding of 'foo;bar;bats'", "foo\\;bar\\;bats", icalvalue_as_ical_string(v));

    icalvalue_free(v);

    v = icalvalue_new_text("foo\\;b\nar\\;ba\tts");
    if (VERBOSE)
        printf("%s\n", icalvalue_as_ical_string(v));

    str_is("test encoding of 'foo\\\\;b\\nar\\\\;ba\\tts'",
           "foo\\\\\\;b\\nar\\\\\\;ba	ts", icalvalue_as_ical_string(v));

    icalvalue_free(v);
}

/* Check RFC6868 encoding of "unsafe" chars in parameter values, such as '\n' */
void test_tzid_escape(void)
{
    icalparameter *tzid;
    icalproperty *prop;

    tzid = icalparameter_new_tzid("Timezone\nwith a newline");
    prop = icalproperty_new_dtstart(icaltime_from_day_of_year(26, 2009));
    icalproperty_add_parameter(prop, tzid);

    if (VERBOSE)
        printf("%s\n", icalproperty_as_ical_string(prop));

    str_is("test encoding of 'Timezone\\nwith a newline'",
           icalproperty_as_ical_string(prop),
           "DTSTART;VALUE=DATE;TZID=Timezone^nwith a newline:20090126\r\n");

    icalproperty_free(prop);
}

void test_requeststat(void)
{
    icalcomponent *c;
    icalproperty *p;
    icalrequeststatus s;
    struct icalreqstattype st, st2;
    char temp[1024];

    static const char test_icalcomp_str[] =
        "BEGIN:VEVENT\n"
        "REQUEST-STATUS:2.1;Success but fallback taken  on one or more property  values.;booga\n"
        "END:VEVENT\n";

    s = icalenum_num_to_reqstat(2, 1);

    ok("icalenum_num_to_reqstat(2,1)", (s == ICAL_2_1_FALLBACK_STATUS));

    ok("icalenum_reqstat_major()", (icalenum_reqstat_major(s) == 2));
    ok("icalenum_reqstat_minor()", (icalenum_reqstat_minor(s) == 1));

    str_is("icalenum_reqstat_desc() -> 2.1", icalenum_reqstat_desc(s),
           "Success but fallback taken  on one or more property  values.");

    st.code = s;
    st.debug = "booga";
    st.desc = 0;

    str_is("icalreqstattype_as_string()",
           icalreqstattype_as_string(st),
           "2.1;Success but fallback taken  on one or more property  values.;booga");

    st.desc = " A non-standard description";

    str_is("icalreqstattype_as_string() w/ non standard description",
           icalreqstattype_as_string(st), "2.1; A non-standard description;booga");

    st.desc = 0;

    snprintf(temp, sizeof(temp), "%s\n", icalreqstattype_as_string(st));

    st2 = icalreqstattype_from_string(
              "2.1;Success but fallback taken  on one or more property  values.;booga");

    /*    printf("%d --  %d --  %s -- %s\n", */
    ok("icalenum_reqstat_major()", (icalenum_reqstat_major(st2.code) == 2));
    ok("icalenum_reqstat_minor()", (icalenum_reqstat_minor(st2.code) == 1));
    str_is("icalenum_reqstat_desc",
           icalenum_reqstat_desc(st2.code),
           "Success but fallback taken  on one or more property  values.");

    st2 = icalreqstattype_from_string(
              "2.1;Success but fallback taken  on one or more property  values.;booga");
    if (VERBOSE) {
        printf("%s\n", icalreqstattype_as_string(st2));
    }

    st2 = icalreqstattype_from_string(
              "2.1;Success but fallback taken  on one or more property  values.;");
    if (VERBOSE) {
        printf("%s\n", icalreqstattype_as_string(st2));
    }

    st2 = icalreqstattype_from_string(
              "2.1;Success but fallback taken  on one or more property  values.");
    if (VERBOSE) {
        printf("%s\n", icalreqstattype_as_string(st2));
    }

    st2 = icalreqstattype_from_string("2.1;");
    if (VERBOSE) {
        printf("%s\n", icalreqstattype_as_string(st2));
    }

    str_is("st2 test again",
           icalreqstattype_as_string(st2),
           "2.1;Success but fallback taken  on one or more property  values.");

    st2 = icalreqstattype_from_string("2.1");
    str_is("st2 test #3",
           icalreqstattype_as_string(st2),
           "2.1;Success but fallback taken  on one or more property  values.");

    c = icalparser_parse_string((char *)test_icalcomp_str);
    ok("icalparser_parse_string()", (c != NULL));
    if (!c) {
        exit(EXIT_FAILURE);
    }

    if (VERBOSE) {
        printf("%s", icalcomponent_as_ical_string(c));
    }
    p = icalcomponent_get_first_property(c, ICAL_REQUESTSTATUS_PROPERTY);

#if ADD_TESTS_REQUIRING_INVESTIGATION
    str_is("icalproperty_new_from_string()",
           icalproperty_as_ical_string(p),
           "REQUEST-STATUS:2.1;Success but fallback taken  on one or more property  \r\n values.;booga\r\n");
#endif
    icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR, ICAL_ERROR_NONFATAL);
    st2 = icalreqstattype_from_string("16.4");

    ok("test unknown code", (st2.code == ICAL_UNKNOWN_STATUS));

    st2 = icalreqstattype_from_string("1.");

    ok("test malformed code", (st2.code == ICAL_UNKNOWN_STATUS));
    icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR, ICAL_ERROR_DEFAULT);

    icalproperty_free(p);
    icalcomponent_free(c);
}

void test_dtstart(void)
{
    struct icaltimetype tt, tt2;

    icalproperty *p;

    tt = icaltime_from_string("19970101");

    int_is("19970101 is a date", tt.is_date, 1);

    p = icalproperty_new_dtstart(tt);

    if (VERBOSE)
        printf("%s\n", icalvalue_kind_to_string(icalvalue_isa(icalproperty_get_value(p))));

    ok("ICAL_DATE_VALUE", (icalvalue_isa(icalproperty_get_value(p)) == ICAL_DATE_VALUE));

    tt2 = icalproperty_get_dtstart(p);
    int_is("converted date is date", tt2.is_date, 1);

    if (VERBOSE)
        printf("%s\n", icalproperty_as_ical_string(p));

    tt = icaltime_from_string("19970101T103000");

    int_is("19970101T103000 is not a date", tt.is_date, 0);

    icalproperty_free(p);

    p = icalproperty_new_dtstart(tt);

    if (VERBOSE) {
        printf("%s\n", icalvalue_kind_to_string(icalvalue_isa(icalproperty_get_value(p))));
    }
    ok("ICAL_DATETIME_VALUE", (icalvalue_isa(icalproperty_get_value(p)) == ICAL_DATETIME_VALUE));

    tt2 = icalproperty_get_dtstart(p);
    int_is("converted datetime is not date", tt2.is_date, 0);

    if (VERBOSE)
        printf("%s\n", icalproperty_as_ical_string(p));

    icalproperty_free(p);
}

void do_test_time(const char *zone)
{
    struct icaltimetype ictt, icttutc, icttzone, icttdayl,
        icttla, icttny, icttphoenix, icttlocal, icttnorm;
    time_t tt, tt2, tt_p200;
    time_t offset_tz;
    icalvalue *v;
    short day_of_week, start_day_of_week, day_of_year;
    icaltimezone *azone, *utczone;
    char msg[256];

    icalerror_set_errors_are_fatal(0);

    azone = icaltimezone_get_builtin_timezone(zone);
    utczone = icaltimezone_get_utc_timezone();

    /* Test new API */
    if (VERBOSE) {
        printf("\n---> From time_t \n");
    }

    tt = 1025127869;    /* stick with a constant... Wed, 26 Jun 2002 21:44:29 GMT */

    if (VERBOSE) {
        printf("Orig        : %s\n", ical_timet_string(tt));
    }

    ictt = icaltime_from_timet_with_zone(tt, 0, NULL);
    str_is("Floating time from time_t", ictt_as_string(ictt), "2002-06-26 21:44:29 (floating)");

    ictt = icaltime_from_timet_with_zone(tt, 0, azone);
#if ADD_TESTS_REQUIRING_INVESTIGATION
    ok("icaltime_from_timet_with_zone(tt,0,zone) as zone",
       strncmp(ictt_as_string(ictt), "2002-06-26 21:44:29", 19) == 0);
#endif

    ictt = icaltime_from_timet_with_zone(tt, 0, utczone);

    str_is("icaltime_from_timet_with_zone(tt,0,utc)", ictt_as_string(ictt),
           "2002-06-26 21:44:29 Z UTC");

    if (VERBOSE)
        printf("\n---> Convert from floating \n");

    ictt = icaltime_from_timet_with_zone(tt, 0, NULL);
    icttutc = icaltime_convert_to_zone(ictt, utczone);

    str_is("Convert from floating to UTC", ictt_as_string(icttutc), "2002-06-26 21:44:29 Z UTC");

    icttzone = icaltime_convert_to_zone(ictt, azone);
    ok("Convert from floating to zone",
       (strncmp(ictt_as_string(icttzone), "2002-06-26 21:44:29", 19) == 0));

    if (VERBOSE)
        printf("\n---> Convert from UTC \n");

    ictt = icaltime_from_timet_with_zone(tt, 0, utczone);
    icttutc = icaltime_convert_to_zone(ictt, utczone);

    str_is("Convert from UTC to UTC", ictt_as_string(icttutc), "2002-06-26 21:44:29 Z UTC");

    icttzone = icaltime_convert_to_zone(ictt, azone);

    ok("Convert from UTC to zone (test year/mon only..)",
       (strncmp(ictt_as_string(icttzone), "2002-06-26 21:44:29", 7) == 0));

    tt2 = icaltime_as_timet_with_zone(icttzone, icttzone.zone);

    if (VERBOSE) {
        printf("No conversion: %s\n", ical_timet_string(tt2));
    }
    ok("No conversion at all (test year/mon only)",
       (strncmp(ical_timet_string(tt2), "2002-06-26 21:44:29 Z", 7) == 0));

    ok("test time conversion routines", (tt == tt2));

    if (VERBOSE) {
        printf("\n---> Convert from zone \n");
    }
    ictt = icaltime_from_timet_with_zone(tt, 0, azone);
    icttzone = icaltime_convert_to_zone(ictt, azone);

    if (VERBOSE) {
        printf("To zone      : %s\n", ictt_as_string(icttzone));
    }
    icttutc = icaltime_convert_to_zone(ictt, utczone);

    if (VERBOSE) {
        printf("To UTC      : %s\n", ictt_as_string(icttutc));
    }
    tt2 = icaltime_as_timet(icttutc);

    if (VERBOSE) {
        printf("No conversion: %s\n", ical_timet_string(tt2));
    }
    tt2 = icaltime_as_timet_with_zone(icttutc, azone);

    if (VERBOSE) {
        printf("Back to zone  : %s\n", ical_timet_string(tt2));
    }
    ok("test time conversion, round 2", (tt == tt2));

    ictt = icaltime_from_string("20001103T183030Z");

    tt = icaltime_as_timet(ictt);

    ok("test icaltime -> time_t for 20001103T183030Z", (tt == 973276230));
    /* Fri Nov  3 10:30:30 PST 2000 in PST
       Fri Nov  3 18:30:30 PST 2000 in UTC */

    if (VERBOSE) {
        printf(" Normalize \n");
        printf("Orig (ical) : %s\n", ictt_as_string(ictt));
    }
    icttnorm = ictt;
    icttnorm.second -= 60 * 60 * 24 * 5;
    icttnorm = icaltime_normalize(icttnorm);

    if (VERBOSE) {
        printf("-5d in sec  : %s\n", ictt_as_string(icttnorm));
    }
    icttnorm.day += 60;
    icttnorm = icaltime_normalize(icttnorm);

    if (VERBOSE) {
        printf("+60 d       : %s\n", ictt_as_string(icttnorm));
    }

    /** add test case here.. **/

    if (VERBOSE) {
        printf("\n As time_t \n");
    }

    tt2 = icaltime_as_timet(ictt);

    if (VERBOSE) {
        printf("20001103T183030Z (timet): %s\n", ical_timet_string(tt2));
        printf("20001103T183030Z        : %s\n", ictt_as_string(ictt));
    }

    /** this test is bogus **/
    ok("test normalization", (tt2 == tt));

    icttlocal = icaltime_convert_to_zone(ictt, azone);
    tt2 = icaltime_as_timet(icttlocal);
    if (VERBOSE) {
        printf("20001103T183030  (timet): %s\n", ical_timet_string(tt2));
        printf("20001103T183030         : %s\n", ictt_as_string(icttlocal));
    }

    offset_tz = (time_t) (-icaltimezone_get_utc_offset_of_utc_time(azone, &ictt, 0));
    if (VERBOSE)
        printf("offset_tz               : %ld\n", (long)offset_tz);

    ok("test utc offset", (tt - tt2 == offset_tz));

    /* FIXME with the new API, it's not very useful */
    icttlocal = ictt;
    icaltimezone_convert_time(&icttlocal,
                              icaltimezone_get_utc_timezone(),
                              icaltimezone_get_builtin_timezone(zone));

    if (VERBOSE)
        printf("As local    : %s\n", ictt_as_string(icttlocal));

    if (VERBOSE)
        printf("\n Convert to and from lib c \n");

    if (VERBOSE)
        printf("System time is: %s\n", ical_timet_string(tt));

    v = icalvalue_new_datetime(ictt);

    if (VERBOSE)
        printf("System time from libical: %s\n", icalvalue_as_ical_string(v));

    icalvalue_free(v);

    tt2 = icaltime_as_timet(ictt);

    if (VERBOSE)
        printf("Converted back to libc: %s\n", ical_timet_string(tt2));

    if (VERBOSE)
        printf("\n Incrementing time  \n");

    icttnorm = ictt;

    icttnorm.year++;
    tt2 = icaltime_as_timet(icttnorm);
    if (VERBOSE)
        printf("Add a year: %s\n", ical_timet_string(tt2));

    icttnorm.month += 13;
    tt2 = icaltime_as_timet(icttnorm);
    if (VERBOSE)
        printf("Add 13 months: %s\n", ical_timet_string(tt2));

    icttnorm.second += 90;
    tt2 = icaltime_as_timet(icttnorm);
    if (VERBOSE)
        printf("Add 90 seconds: %s\n", ical_timet_string(tt2));

    if (VERBOSE)
        printf("\n Day Of week \n");

    day_of_week = icaltime_day_of_week(ictt);
    start_day_of_week = icaltime_start_doy_week(ictt, 1);
    day_of_year = icaltime_day_of_year(ictt);

    snprintf(msg, sizeof(msg), "Testing day of week %d", day_of_week);
    int_is(msg, day_of_week, 6);

    snprintf(msg, sizeof(msg), "Testing day of year %d", day_of_year);
    int_is(msg, day_of_year, 308);

    snprintf(msg, sizeof(msg), "Week started on doy of %d", start_day_of_week);
    int_is(msg, start_day_of_week, 303);

    if (VERBOSE)
        printf("\n TimeZone Conversions \n");

/*
    icttla = ictt;
    icaltimezone_convert_time(&icttla,
                              icaltimezone_get_utc_timezone(),
                              lazone);
*/
    icttla = icaltime_convert_to_zone(ictt,
                                      icaltimezone_get_builtin_timezone("America/Los_Angeles"));

#if ADD_TESTS_BROKEN_BUILTIN_TZDATA
    int_is("Converted hour in America/Los_Angeles is 10", icttla.hour, 10);
#endif
    icttutc = icaltime_convert_to_zone(icttla, icaltimezone_get_utc_timezone());

#if ADD_TESTS_BROKEN_BUILTIN_TZDATA
    ok("America/Los_Angeles local time is 2000-11-03 10:30:30",
       (strncmp(ictt_as_string(icttla), "2000-11-03 10:30:30", 19) == 0));
#endif
    ok("Test conversion back to UTC", (icaltime_compare(icttutc, ictt) == 0));

    icttny = icaltime_convert_to_zone(ictt, icaltimezone_get_builtin_timezone("America/New_York"));

    icttphoenix = icaltime_convert_to_zone(ictt,
                                           icaltimezone_get_builtin_timezone("America/Phoenix"));

    if (VERBOSE) {
        printf("Orig (ctime): %s\n", ical_timet_string(tt));
        printf("Orig (ical) : %s\n", ictt_as_string(ictt));
        printf("UTC         : %s\n", ictt_as_string(icttutc));
        printf("Los Angeles : %s\n", ictt_as_string(icttla));
        printf("Phoenix     : %s\n", ictt_as_string(icttphoenix));
        printf("New York    : %s\n", ictt_as_string(icttny));
    }
    /** @todo Check results for Phoenix here?... **/

    /* Daylight savings test for New York */
    if (VERBOSE) {
        printf("\n Daylight Savings \n");

        printf("Orig (ctime): %s\n", ical_timet_string(tt));
        printf("Orig (ical) : %s\n", ictt_as_string(ictt));
        printf("NY          : %s\n", ictt_as_string(icttny));
    }
#if ADD_TESTS_BROKEN_BUILTIN_TZDATA
    ok("Converted time in zone America/New_York is 2000-11-03 13:30:30",
       (strncmp(ictt_as_string(icttny), "2000-11-03 13:30:30", 19) == 0));
#endif
    tt_p200 = tt + 200 * 24 * 60 * 60;  /* Add 200 days */

    icttdayl = icaltime_from_timet_with_zone(tt_p200, 0, icaltimezone_get_utc_timezone());
    icttny = icaltime_convert_to_zone(icttdayl,
                                      icaltimezone_get_builtin_timezone("America/New_York"));

    if (VERBOSE) {
        printf("Orig +200d  : %s\n", ical_timet_string(tt_p200));
        printf("NY+200D     : %s\n", ictt_as_string(icttny));
    }

    ok("Converted time +200d in zone America/New_York is 2001-05-22 14:30:30",
       (strncmp(ictt_as_string(icttny), "2001-05-22 14:30:30", 19) == 0));

    /* Daylight savings test for Los Angeles */

    icttla = icaltime_convert_to_zone(ictt,
                                      icaltimezone_get_builtin_timezone("America/Los_Angeles"));

    if (VERBOSE) {
        printf("\nOrig (ctime): %s\n", ical_timet_string(tt));
        printf("Orig (ical) : %s\n", ictt_as_string(ictt));
        printf("LA          : %s\n", ictt_as_string(icttla));
    }
#if ADD_TESTS_BROKEN_BUILTIN_TZDATA
    ok("Converted time in zone America/Los_Angeles is 2000-11-03 10:30:30",
       (strncmp(ictt_as_string(icttla), "2000-11-03 10:30:30", 19) == 0));
#endif
    icttla = icaltime_convert_to_zone(icttdayl,
                                      icaltimezone_get_builtin_timezone("America/Los_Angeles"));

    if (VERBOSE) {
        printf("Orig +200d  : %s\n", ical_timet_string(tt_p200));
        printf("LA+200D     : %s\n", ictt_as_string(icttla));
    }

    ok("Converted time +200d in zone America/Los_Angeles is 2001-05-22 11:30:30",
       (strncmp(ictt_as_string(icttla), "2001-05-22 11:30:30", 19) == 0));

    icalerror_set_errors_are_fatal(1);

    /* Do some checks for icaltime_compare */
    if (VERBOSE) {
        printf("\n icaltime_compare() tests \n");
    }

    ictt = icaltime_from_string("20001203T183030");
    icttutc = icaltime_convert_to_zone(ictt, utczone);
    icttny = icaltime_convert_to_zone(ictt,
                                      icaltimezone_get_builtin_timezone("America/New_York"));

    int_is("icaltime_compare(): same float and UTC",
           icaltime_compare(ictt, icttutc),
           0);

    int_is("icaltime_compare(): same float and NY",
           icaltime_compare(ictt, icttny),
           0);

    /* The offset is set as from UTC: see issue #231 */
    icttny.hour = icttny.hour - 5;
    int_is("icaltime_compare(): different float and NY",
           icaltime_compare(ictt, icttny),
           1);

    int_is("icaltime_compare(): same UTC and NY",
           icaltime_compare(icttutc, icttny),
           0);
}

void test_iterators(void)
{
    icalcomponent *c, *inner, *next;
    icalcompiter i;
    char vevent_list[64] = "";
    char remaining_list[64] = "";

    const char *vevent_list_good = "12347";
    const char *remaining_list_good = "568910";

    int nomore = 1;

    c = icalcomponent_vanew(ICAL_VCALENDAR_COMPONENT,
                            icalcomponent_vanew(ICAL_VEVENT_COMPONENT,
                                                icalproperty_new_version("1"), (void *)0),
                            icalcomponent_vanew(ICAL_VEVENT_COMPONENT,
                                                icalproperty_new_version("2"), (void *)0),
                            icalcomponent_vanew(ICAL_VEVENT_COMPONENT,
                                                icalproperty_new_version("3"), (void *)0),
                            icalcomponent_vanew(ICAL_VEVENT_COMPONENT,
                                                icalproperty_new_version("4"), (void *)0),
                            icalcomponent_vanew(ICAL_VTODO_COMPONENT,
                                                icalproperty_new_version("5"), (void *)0),
                            icalcomponent_vanew(ICAL_VJOURNAL_COMPONENT,
                                                icalproperty_new_version("6"), (void *)0),
                            icalcomponent_vanew(ICAL_VEVENT_COMPONENT,
                                                icalproperty_new_version("7"), (void *)0),
                            icalcomponent_vanew(ICAL_VJOURNAL_COMPONENT,
                                                icalproperty_new_version("8"), (void *)0),
                            icalcomponent_vanew(ICAL_VJOURNAL_COMPONENT,
                                                icalproperty_new_version("9"), (void *)0),
                            icalcomponent_vanew(ICAL_VJOURNAL_COMPONENT,
                                                icalproperty_new_version("10"), (void *)0),
                            (void *)0);

    /* List all of the VEVENTS */

    for (i = icalcomponent_begin_component(c, ICAL_VEVENT_COMPONENT);
         icalcompiter_deref(&i) != 0; icalcompiter_next(&i)) {

        icalcomponent *this = icalcompiter_deref(&i);

        icalproperty *p = icalcomponent_get_first_property(this,
                                                           ICAL_VERSION_PROPERTY);
        const char *s = icalproperty_get_version(p);

        strncat(vevent_list, s, sizeof(vevent_list) - strlen(vevent_list) - 1);
    }
    str_is("iterate through VEVENTS in a component", vevent_list, vevent_list_good);

    /* Delete all of the VEVENTS */
    /* reset iterator */
    (void)icalcomponent_get_first_component(c, ICAL_VEVENT_COMPONENT);

    while ((inner = icalcomponent_get_current_component(c)) != 0) {
        if (icalcomponent_isa(inner) == ICAL_VEVENT_COMPONENT) {
            icalcomponent_remove_component(c, inner);
            icalcomponent_free(inner);
        } else {
            (void)icalcomponent_get_next_component(c, ICAL_VEVENT_COMPONENT);
        }
    }

    /* List all remaining components */
    for (inner = icalcomponent_get_first_component(c, ICAL_ANY_COMPONENT);
         inner != 0; inner = icalcomponent_get_next_component(c, ICAL_ANY_COMPONENT)) {

        icalproperty *p = icalcomponent_get_first_property(inner, ICAL_VERSION_PROPERTY);

        const char *s = icalproperty_get_version(p);

        strcat(remaining_list, s);
    }

    str_is("iterate through remaining components", remaining_list, remaining_list_good);

    /* Remove all remaining components */
    for (inner = icalcomponent_get_first_component(c, ICAL_ANY_COMPONENT);
         inner != 0;
         inner = next) {

        icalcomponent *this;
        icalproperty *p;

        next = icalcomponent_get_next_component(c, ICAL_ANY_COMPONENT);

        p = icalcomponent_get_first_property(inner, ICAL_VERSION_PROPERTY);
        (void)icalproperty_get_version(p);

        icalcomponent_remove_component(c, inner);

        this = icalcomponent_get_current_component(c);

        if (this != 0) {
            p = icalcomponent_get_first_property(this, ICAL_VERSION_PROPERTY);
            (void)icalproperty_get_version(p);
        }

        icalcomponent_free(inner);
    }

    /* List all remaining components */
    for (inner = icalcomponent_get_first_component(c, ICAL_ANY_COMPONENT);
         inner != 0; inner = icalcomponent_get_next_component(c, ICAL_ANY_COMPONENT)) {

        icalproperty *p = icalcomponent_get_first_property(inner, ICAL_VERSION_PROPERTY);

        const char *s = icalproperty_get_version(p);

        if (s)
            nomore = 0;
    }

    ok("test if any components remain after deleting the rest", nomore == 1);

    icalcomponent_free(c);
}

void test_time(void)
{
    const char *zones[6] =
    {
        "America/Los_Angeles", "America/New_York", "Europe/London", "Asia/Shanghai", NULL
    };

    int i;

    do_test_time(0);

    for (i = 0; zones[i] != NULL; i++) {

        if (VERBOSE)
            printf(" ######### Timezone: %s ############\n", zones[i]);

        do_test_time(zones[i]);
    }
}

void test_icalset(void)
{
    icalcomponent *c;

    icalset *f = icalset_new_file("2446.ics");
    icalset *d = icalset_new_dir("outdir");

    assert(f != 0);
    assert(d != 0);

    for (c = icalset_get_first_component(f); c != 0; c = icalset_get_next_component(f)) {

        icalcomponent *clone;

        clone = icalcomponent_new_clone(c);

        (void)icalset_add_component(d, clone);

        printf(" class %d\n", icalclassify(c, 0, "user"));
    }

    icalset_free(f);
    icalset_free(d);
}

void test_overlaps(void)
{
    icalcomponent *cset, *c;
    icalset *set;
    time_t tm1 = 973378800;     /*Sat Nov  4 23:00:00 UTC 2000,
                                   Sat Nov  4 15:00:00 PST 2000 */
    time_t tm2 = 973382400;     /*Sat Nov  5 00:00:00 UTC 2000
                                   Sat Nov  4 16:00:00 PST 2000 */

    time_t hh = 1800;   /* one half hour */

    icalfileset_options options = { O_RDONLY, 0644, 0, NULL };
    set = icalset_new(ICAL_FILE_SET, TEST_DATADIR "/overlaps.ics", &options);

    c = icalcomponent_vanew(ICAL_VEVENT_COMPONENT,
                            icalproperty_vanew_dtstart(icaltime_from_timet_with_zone(tm1 - hh, 0, NULL), (void *)0),
                            icalproperty_vanew_dtend(icaltime_from_timet_with_zone(tm2 - hh, 0, NULL), (void *)0),
                            0);

    cset = icalclassify_find_overlaps(set, c);
#if ADD_TESTS_REQUIRING_INVESTIGATION
    ok("TODO find overlaps 1", (cset != NULL));
#endif

    if (VERBOSE && cset) {
        printf("%s\n", icalcomponent_as_ical_string(cset));
    }

    if (cset)
        icalcomponent_free(cset);

    if (c)
        icalcomponent_free(c);

    c = icalcomponent_vanew(
            ICAL_VEVENT_COMPONENT,
            icalproperty_vanew_dtstart(icaltime_from_timet_with_zone(tm1 - hh, 0, NULL), (void *)0),
            icalproperty_vanew_dtend(icaltime_from_timet_with_zone(tm2, 0, NULL), (void *)0),
            0);

    cset = icalclassify_find_overlaps(set, c);

#if ADD_TESTS_REQUIRING_INVESTIGATION
    ok("TODO find overlaps 1", cset != NULL);
#endif
    if (VERBOSE && cset)
        printf("%s\n", icalcomponent_as_ical_string(cset));

    if (cset)
        icalcomponent_free(cset);

    if (c)
        icalcomponent_free(c);

    c = icalcomponent_vanew(
            ICAL_VEVENT_COMPONENT,
            icalproperty_vanew_dtstart(icaltime_from_timet_with_zone(tm1 + 5 * hh, 0, NULL), (void *)0),
            icalproperty_vanew_dtend(icaltime_from_timet_with_zone(tm2 + 5 * hh, 0, NULL), (void *)0),
            0);

    cset = icalclassify_find_overlaps(set, c);
#if ADD_TESTS_REQUIRING_INVESTIGATION
    ok("TODO find overlaps 1", cset != NULL);
#endif
    if (VERBOSE && cset)
        printf("%s\n", icalcomponent_as_ical_string(cset));

    if (set)
        icalset_free(set);

    if (cset)
        icalcomponent_free(cset);

    if (c)
        icalcomponent_free(c);
}

void test_fblist(void)
{
    icalspanlist *sl, *new_sl;
    icalfileset_options options = { O_RDONLY, 0644, 0, NULL };
    icalset *set = icalset_new(ICAL_FILE_SET, TEST_DATADIR "/spanlist.ics", &options);
    struct icalperiodtype period;
    icalcomponent *comp, *fbcomp;
    int *foo;
    int i;

    sl = icalspanlist_new(set,
                          icaltime_from_string("19980101T000000Z"),
                          icaltime_from_string("19980108T000000Z"));
    ok("create initial spanlist", (sl != NULL));

    ok("open ../test-data/spanlist.ics", (set != NULL));
    assert(set != NULL);

    if (VERBOSE) {
        printf("Restricted spanlist\n");
    }
    if (VERBOSE) {
        icalspanlist_dump(sl);
    }

    period = icalspanlist_next_free_time(sl, icaltime_from_string("19970801T120000"));

    str_is("Next Free time start 19970801T120000", icaltime_as_ical_string(period.start),
           "19970801T120000");
    str_is("Next Free time end   19980101T000000", icaltime_as_ical_string(period.end),
           "19980101T000000");

    period = icalspanlist_next_free_time(sl, period.end);

    str_is("Next Free time start 19980101T010000", icaltime_as_ical_string(period.start),
           "19980101T010000");
    str_is("Next Free time end   19980102T010000", icaltime_as_ical_string(period.end),
           "19980102T010000");

    if (VERBOSE) {
        fbcomp = icalspanlist_as_vfreebusy(sl, "a@foo.com", "b@foo.com");
        printf("%s\n", icalcomponent_as_ical_string(fbcomp));
        icalcomponent_free(fbcomp);
    }

    foo = icalspanlist_as_freebusy_matrix(sl, 3600);

    for (i = 0; foo[i] != -1; i++);     /* find number entries */

    int_is("Calculating freebusy hourly matrix", i, (7 * 24));

    if (VERBOSE) {
        for (i = 0; foo[i] != -1; i++) {
            printf("%d", foo[i]);
            if ((i % 24) == 23)
                printf("\n");
        }
        printf("\n\n");
    }

    free(foo);

    foo = icalspanlist_as_freebusy_matrix(sl, 3600 * 24);

    ok("Calculating daily freebusy matrix", (foo != NULL));

    if (foo) {
        char out_str[80] = "";
        char *strp = out_str;

        for (i = 0; foo[i] != -1; i++) {
            snprintf(strp, 79-i, "%1d", foo[i]);
            strp++;
        }
        str_is("Checking freebusy validity", out_str, "1121110");

    if (VERBOSE) {
        for (i = 0; foo[i] != -1; i++) {
            printf("%d", foo[i]);
            if ((i % 7) == 6)
                printf("\n");
            }
            printf("\n\n");
        }
        free(foo);
    }

    icalspanlist_free(sl);

    if (VERBOSE) {
        printf("Unrestricted spanlist\n");
    }

    sl = icalspanlist_new(set, icaltime_from_string("19970324T120000Z"), icaltime_null_time());

    ok("add 19970324T120000Z to spanlist", (sl != NULL));

    if (VERBOSE) {
        printf("Restricted spanlist\n");
    }
    if (VERBOSE) {
        icalspanlist_dump(sl);
    }

    period = icalspanlist_next_free_time(sl, icaltime_from_string("19970801T120000Z"));

    str_is("Next Free time start 19980101T010000",
           icaltime_as_ical_string(period.start), "19980101T010000");

    str_is("Next Free time end   19980102T010000",
           icaltime_as_ical_string(period.end), "19980102T010000");

    comp = icalspanlist_as_vfreebusy(sl, "a@foo.com", "b@foo.com");

    ok("Calculating VFREEBUSY component", (comp != NULL));
    if (VERBOSE) {
        printf("%s\n", icalcomponent_as_ical_string(comp));
    }

    new_sl = icalspanlist_from_vfreebusy(comp);

    ok("Calculating spanlist from generated VFREEBUSY component", (new_sl != NULL));

    if (VERBOSE) {
        icalspanlist_dump(new_sl);
    }

    icalspanlist_free(sl);

    icalspanlist_free(new_sl);

    icalcomponent_free(comp);

    icalset_free(set);
}

void test_convenience(void)
{
    icalcomponent *c;
    int duration;
    struct icaltimetype tt;

    c = icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
            icalcomponent_vanew(
                ICAL_VEVENT_COMPONENT,
                icalproperty_new_dtstart(icaltime_from_string("19970801T120000")),
                icalproperty_new_dtend(icaltime_from_string("19970801T130000")),
                (void *)0),
            (void *)0);

    if (VERBOSE)
        printf("\n%s\n", icalcomponent_as_ical_string(c));

    duration = icaldurationtype_as_int(icalcomponent_get_duration(c)) / 60;

    str_is("Start is 1997-08-01 12:00:00 (floating)",
           ictt_as_string(icalcomponent_get_dtstart(c)), "1997-08-01 12:00:00 (floating)");
    str_is("End is 1997-08-01 13:00:00 (floating)",
           ictt_as_string(icalcomponent_get_dtend(c)), "1997-08-01 13:00:00 (floating)");
    ok("Duration is 60 m", (duration == 60));

    icalcomponent_free(c);

    c = icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
            icalcomponent_vanew(
                ICAL_VEVENT_COMPONENT,
                icalproperty_new_dtstart(icaltime_from_string("19970801T120000Z")),
                icalproperty_new_duration(icaldurationtype_from_string("PT1H30M")),
                (void *)0),
            (void *)0);

    if (VERBOSE)
        printf("\n%s\n", icalcomponent_as_ical_string(c));

    duration = icaldurationtype_as_int(icalcomponent_get_duration(c)) / 60;

    str_is("Start is 1997-08-01 12:00:00 Z UTC",
           ictt_as_string(icalcomponent_get_dtstart(c)), "1997-08-01 12:00:00 Z UTC");
    str_is("End is 1997-08-01 13:30:00 Z UTC",
           ictt_as_string(icalcomponent_get_dtend(c)), "1997-08-01 13:30:00 Z UTC");
    ok("Duration is 90 m", (duration == 90));

    icalcomponent_free(c);

    icalerror_set_errors_are_fatal(0);

    c = icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
            icalcomponent_vanew(
                ICAL_VEVENT_COMPONENT,
                icalproperty_new_dtstart(icaltime_from_string("19970801T120000")),
                icalproperty_new_dtend(icaltime_from_string("19970801T130000")),
                (void *)0),
            (void *)0);

    icalcomponent_set_duration(c, icaldurationtype_from_string("PT1H30M"));

    if (VERBOSE)
        printf("\n%s\n", icalcomponent_as_ical_string(c));

    duration = icaldurationtype_as_int(icalcomponent_get_duration(c)) / 60;

    str_is("Start is 1997-08-01 12:00:00 (floating)",
           ictt_as_string(icalcomponent_get_dtstart(c)), "1997-08-01 12:00:00 (floating)");
    str_is("End is 1997-08-01 13:00:00 (floating)",
           ictt_as_string(icalcomponent_get_dtend(c)), "1997-08-01 13:00:00 (floating)");
    ok("Duration is 60 m", (duration == 60));

    icalcomponent_free(c);

    c = icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
            icalcomponent_vanew(
                ICAL_VEVENT_COMPONENT,
                icalproperty_new_dtstart(icaltime_from_string("19970801T120000Z")),
                icalproperty_new_duration(icaldurationtype_from_string("PT1H30M")),
                (void *)0),
            (void *)0);

    icalcomponent_set_dtend(c, icaltime_from_string("19970801T133000Z"));

    if (VERBOSE)
        printf("\n%s\n", icalcomponent_as_ical_string(c));

    duration = icaldurationtype_as_int(icalcomponent_get_duration(c)) / 60;

    ok("Start is 1997-08-01 12:00:00 Z UTC",
       (0 == strcmp("1997-08-01 12:00:00 Z UTC", ictt_as_string(icalcomponent_get_dtstart(c)))));
    ok("End is 1997-08-01 13:30:00 Z UTC",
       (0 == strcmp("1997-08-01 13:30:00 Z UTC", ictt_as_string(icalcomponent_get_dtend(c)))));
    ok("Duration is 90 m", (duration == 90));

    icalerror_set_errors_are_fatal(1);

    icalcomponent_free(c);

    c = icalcomponent_vanew(ICAL_VCALENDAR_COMPONENT,
                            icalcomponent_vanew(ICAL_VEVENT_COMPONENT, (void *)0), (void *)0);

    icalcomponent_set_dtstart(c, icaltime_from_string("19970801T120000Z"));
    icalcomponent_set_dtend(c, icaltime_from_string("19970801T133000Z"));

    if (VERBOSE)
        printf("\n%s\n", icalcomponent_as_ical_string(c));

    duration = icaldurationtype_as_int(icalcomponent_get_duration(c)) / 60;

    ok("Start is 1997-08-01 12:00:00 Z UTC",
       (0 == strcmp("1997-08-01 12:00:00 Z UTC", ictt_as_string(icalcomponent_get_dtstart(c)))));
    ok("End is 1997-08-01 13:30:00 Z UTC",
       (0 == strcmp("1997-08-01 13:30:00 Z UTC", ictt_as_string(icalcomponent_get_dtend(c)))));
    ok("Duration is 90 m", (duration == 90));

    icalcomponent_free(c);

    c = icalcomponent_vanew(ICAL_VCALENDAR_COMPONENT,
                            icalcomponent_vanew(ICAL_VEVENT_COMPONENT, (void *)0), (void *)0);

    icalcomponent_set_dtstart(c, icaltime_from_string("19970801T120000Z"));
    icalcomponent_set_duration(c, icaldurationtype_from_string("PT1H30M"));

    if (VERBOSE)
        printf("\n%s\n", icalcomponent_as_ical_string(c));

    duration = icaldurationtype_as_int(icalcomponent_get_duration(c)) / 60;

    ok("Start is 1997-08-01 12:00:00 Z UTC",
       (0 == strcmp("1997-08-01 12:00:00 Z UTC", ictt_as_string(icalcomponent_get_dtstart(c)))));
    ok("End is 1997-08-01 13:30:00 Z UTC",
       (0 == strcmp("1997-08-01 13:30:00 Z UTC", ictt_as_string(icalcomponent_get_dtend(c)))));
    ok("Duration is 90 m", (duration == 90));

    icalcomponent_free(c);

    c = icalcomponent_vanew(ICAL_VCALENDAR_COMPONENT,
                            icalcomponent_vanew(ICAL_VEVENT_COMPONENT, (void *)0), (void *)0);

    tt = icaltime_from_string("19970801T120000");
    (void)icaltime_set_timezone(&tt, icaltimezone_get_builtin_timezone("Europe/Rome"));
    icalcomponent_set_dtstart(c, tt);

    if (VERBOSE)
        printf("\n%s\n", icalcomponent_as_ical_string(c));

    icalcomponent_set_duration(c, icaldurationtype_from_string("PT1H30M"));
    duration = icaldurationtype_as_int(icalcomponent_get_duration(c)) / 60;

#if ADD_TESTS_BROKEN_BUILTIN_TZDATA
    ok("Start is 1997-08-01 12:00:00 Europe/Rome",
       (0 == strcmp("1997-08-01 12:00:00 " TESTS_TZID_PREFIX "Europe/Rome",
                    ictt_as_string(icalcomponent_get_dtstart(c)))));
    ok("End is 1997-08-01 13:30:00 Europe/Rome",
       (0 == strcmp("1997-08-01 13:30:00 " TESTS_TZID_PREFIX "Europe/Rome",
                    ictt_as_string(icalcomponent_get_dtend(c)))));
#endif
    ok("Duration is 90 m", (duration == 90));

    icalcomponent_free(c);
}

void test_time_parser(void)
{
    struct icaltimetype tt;

    icalerror_set_errors_are_fatal(0);

    tt = icaltime_from_string("19970101T1000");
    ok("19970101T1000 is null time", icaltime_is_null_time(tt));

    tt = icaltime_from_string("19970101X100000");
    ok("19970101X100000 is null time", icaltime_is_null_time(tt));

    tt = icaltime_from_string("19970101T100000");
    ok("19970101T100000 is valid", !icaltime_is_null_time(tt));

    if (VERBOSE)
        printf("%s\n", icaltime_as_ctime(tt));

    tt = icaltime_from_string("19970101T100000Z");

    ok("19970101T100000Z is valid", !icaltime_is_null_time(tt));
    if (VERBOSE)
        printf("%s\n", icaltime_as_ctime(tt));

    tt = icaltime_from_string("19970101");
    ok("19970101 is valid", (!icaltime_is_null_time(tt)));

    if (VERBOSE)
        printf("%s\n", icaltime_as_ctime(tt));

    icalerror_set_errors_are_fatal(1);
}

void test_recur_parser(void)
{
    struct icalrecurrencetype rt;
    icalvalue *v = NULL;
    icalerrorstate es;
    const char *str;

    str =
        "FREQ=YEARLY;UNTIL=20000131T090000Z;BYDAY=-1TU,3WE,-4FR,SA,SU;BYYEARDAY=34,65,76,78;BYMONTH=1,2,3,4,8";
    rt = icalrecurrencetype_from_string(str);
    str_is(str, icalrecurrencetype_as_string(&rt), str);

    /* Add COUNT and make sure its ignored in lieu of UNTIL */
    rt.count = 3;
    str_is(str, icalrecurrencetype_as_string(&rt), str);

    /* Try to create a new RRULE value with UNTIL + COUNT */
    es = icalerror_supress("BADARG");
    v = icalvalue_new_recur(rt);
    rt = icalvalue_get_recur(v);
    icalerror_restore("BADARG", es);
    ok("COUNT + UNTIL not allowed", rt.freq == ICAL_NO_RECURRENCE);

    str = "FREQ=DAILY;COUNT=3;BYDAY=-1TU,3WE,-4FR,SA,SU;BYYEARDAY=34,65,76,78;BYMONTH=1,2,3,4,8";

    rt = icalrecurrencetype_from_string(str);
    str_is(str, icalrecurrencetype_as_string(&rt), str);

    /* Try to parse an RRULE value with UNTIL + COUNT */
    str = "FREQ=YEARLY;UNTIL=20000131T090000Z;COUNT=3";

    es = icalerror_supress("MALFORMEDDATA");
    rt = icalrecurrencetype_from_string(str);
    icalerror_restore("MALFORMEDDATA", es);
    ok(str, rt.freq == ICAL_NO_RECURRENCE);
    free(v);
}

char *ical_strstr(const char *haystack, const char *needle)
{
    return strstr(haystack, needle);
}

void test_start_of_week(void)
{
    struct icaltimetype tt2;
    struct icaltimetype tt1 = icaltime_from_string("19900110");
    int dow, doy, start_dow;

    do {
        tt1 = icaltime_normalize(tt1);

        doy = icaltime_start_doy_week(tt1, 1);
        dow = icaltime_day_of_week(tt1);

        tt2 = icaltime_from_day_of_year(doy, tt1.year);
        start_dow = icaltime_day_of_week(tt2);

        if (doy == 1) {
            char msg[128];

            snprintf(msg, sizeof(msg), "%s", ictt_as_string(tt1));
            int_is(msg, start_dow, 1);
        }

        if (start_dow != 1) {   /* Sunday is 1 */
            printf("failed: Start of week (%s) is not a Sunday \n for %s (doy=%d,dow=%d)\n",
                   ictt_as_string(tt2), ictt_as_string(tt1), dow, start_dow);
        }

        assert(start_dow == 1);

        tt1.day += 1;

    } while (tt1.year < 2010);
}

void test_doy(void)
{
    struct icaltimetype tt1, tt2;
    short doy, doy2;
    char msg[128];

    tt1 = icaltime_from_string("19900101");

    if (VERBOSE)
        printf("Test icaltime_day_of_year() agreement with mktime\n");

    do {
        struct tm stm;

        tt1 = icaltime_normalize(tt1);

        stm.tm_sec = tt1.second;
        stm.tm_min = tt1.minute;
        stm.tm_hour = tt1.hour;
        stm.tm_mday = tt1.day;
        stm.tm_mon = tt1.month - 1;
        stm.tm_year = tt1.year - 1900;
        stm.tm_isdst = -1;

        (void)mktime(&stm);

        doy = icaltime_day_of_year(tt1);

        doy2 = stm.tm_yday + 1;

        if (doy == 1) {
          /** show some test cases **/
            snprintf(msg, sizeof(msg), "Year %d - mktime() compare", tt1.year);
            int_is(msg, doy, doy2);
        }

        if (doy != doy2) {
            printf("Failed for %s (%d,%d)\n", ictt_as_string(tt1), doy, doy2);
        }
        assert(doy == doy2);

        tt1.day += 1;

    } while (tt1.year < 2010);

    if (VERBOSE)
        printf("\nTest icaltime_day_of_year() agreement with icaltime_from_day_of_year()\n");

    tt1 = icaltime_from_string("19900101");

    do {
        if (doy == 1) {
          /** show some test cases **/
            snprintf(msg, sizeof(msg), "Year %d - icaltime_day_of_year() compare", tt1.year);
            int_is(msg, doy, doy2);
        }

        doy = icaltime_day_of_year(tt1);
        tt2 = icaltime_from_day_of_year(doy, tt1.year);
        doy2 = icaltime_day_of_year(tt2);

        assert(doy2 == doy);
        assert(icaltime_compare(tt1, tt2) == 0);

        tt1.day += 1;
        tt1 = icaltime_normalize(tt1);

    } while (tt1.year < 2010);

    tt1 = icaltime_from_string("19950301");
    doy = icaltime_day_of_year(tt1);
    tt2 = icaltime_from_day_of_year(doy, 1995);
    if (VERBOSE) {
        printf("%d %s %s\n", doy, icaltime_as_ctime(tt1), icaltime_as_ctime(tt2));
    }
    ok("test 19950301", (tt2.day == 1 && tt2.month == 3));
    ok("day of year == 60", (doy == 60));

    tt1 = icaltime_from_string("19960301");
    doy = icaltime_day_of_year(tt1);
    tt2 = icaltime_from_day_of_year(doy, 1996);
    if (VERBOSE) {
        printf("%d %s %s\n", doy, icaltime_as_ctime(tt1), icaltime_as_ctime(tt2));
    }
    ok("test 19960301", (tt2.day == 1 && tt2.month == 3));
    ok("day of year == 61", (doy == 61));

    tt1 = icaltime_from_string("19970301");
    doy = icaltime_day_of_year(tt1);
    tt2 = icaltime_from_day_of_year(doy, 1997);
    if (VERBOSE) {
        printf("%d %s %s\n", doy, icaltime_as_ctime(tt1), icaltime_as_ctime(tt2));
    }
    ok("test 19970301", (tt2.day == 1 && tt2.month == 3));
    ok("day of year == 60", (doy == 60));
}

void test_x(void)
{
    static const char test_icalcomp_str[] =
        "BEGIN:VEVENT\r\n"
        "RRULE\r\n"
        " ;X-EVOLUTION-ENDDATE=20030209T081500\r\n"
        " :FREQ=DAILY;COUNT=10;INTERVAL=6\r\n" "X-COMMENT;X-FOO=BAR: Booga\r\n" "END:VEVENT\r\n";

    icalcomponent *icalcomp;
    icalproperty *prop;
    struct icalrecurrencetype recur;
    int n_errors;

    icalcomp = icalparser_parse_string((char *)test_icalcomp_str);
    assert(icalcomp != NULL);

    if (VERBOSE)
        printf("%s\n\n", icalcomponent_as_ical_string(icalcomp));

    n_errors = icalcomponent_count_errors(icalcomp);
    int_is("icalparser_parse_string()", n_errors, 0);

    if (n_errors) {
      /** NOT USED **/
        icalproperty *p;

        for (p = icalcomponent_get_first_property(icalcomp, ICAL_XLICERROR_PROPERTY);
             p;
             p = icalcomponent_get_next_property(icalcomp, ICAL_XLICERROR_PROPERTY)) {
            const char *str;
            str = icalproperty_as_ical_string(p);
            fprintf(stderr, "error: %s\n", str);
        }
    }

    prop = icalcomponent_get_first_property(icalcomp, ICAL_RRULE_PROPERTY);
    ok("get RRULE property", (prop != NULL));
    assert(prop != NULL);

    recur = icalproperty_get_rrule(prop);

    if (VERBOSE)
        printf("%s\n", icalrecurrencetype_as_string(&recur));

    icalcomponent_free(icalcomp);
}

void test_gauge_sql(void)
{
    icalgauge *g;
    const char *str;

    str =
        "SELECT DTSTART,DTEND,COMMENT FROM VEVENT,VTODO WHERE VEVENT.SUMMARY = 'Bongoa' AND SEQUENCE < 5";

    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != NULL));
    if (VERBOSE)
        icalgauge_dump(g);

    icalgauge_free(g);

    str =
        "SELECT * FROM VEVENT,VTODO WHERE VEVENT.SUMMARY = 'Bongoa' AND SEQUENCE < 5 OR METHOD != 'CREATE'";

    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != NULL));
    if (VERBOSE)
        icalgauge_dump(g);

    icalgauge_free(g);

    str = "SELECT * FROM VEVENT WHERE SUMMARY == 'BA301'";

    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != NULL));
    if (VERBOSE)
        icalgauge_dump(g);

    icalgauge_free(g);

    str = "SELECT * FROM VEVENT WHERE SUMMARY == 'BA301'";

    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != NULL));
    if (VERBOSE)
        icalgauge_dump(g);

    icalgauge_free(g);

    str = "SELECT * FROM VEVENT WHERE LOCATION == '104 Forum'";

    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != NULL));
    if (VERBOSE)
        icalgauge_dump(g);

    icalgauge_free(g);
}

void test_gauge_compare(void)
{
    icalgauge *g;
    icalcomponent *c;
    const char *str;

    /* Equality */

    c = icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
            icalcomponent_vanew(
                ICAL_VEVENT_COMPONENT,
                icalproperty_new_dtstart(icaltime_from_string("20000101T000002")), 0),
            (void *)0);

    str = "SELECT * FROM VEVENT WHERE DTSTART = '20000101T000002'";
    g = icalgauge_new_from_sql(str, 0);

    ok(str, (c != 0 && g != 0));
    assert(c != 0);
    assert(g != 0);

    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }
    str = "SELECT * FROM VEVENT WHERE DTSTART = '20000101T000001'";
    g = icalgauge_new_from_sql(str, 0);

    ok(str, (g != 0));

    assert(g != 0);
    int_is("compare", icalgauge_compare(g, c), 0);

    icalgauge_free(g);

    str = "SELECT * FROM VEVENT WHERE DTSTART != '20000101T000003'";
    g = icalgauge_new_from_sql(str, 0);

    ok(str, (c != 0 && g != 0));

    assert(g != 0);
    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }

    /* Less than */

    str = "SELECT * FROM VEVENT WHERE DTSTART < '20000101T000003'";
    g = icalgauge_new_from_sql(str, 0);

    ok(str, (c != 0 && g != 0));

    assert(g != 0);
    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }
    str = "SELECT * FROM VEVENT WHERE DTSTART < '20000101T000002'";
    g = icalgauge_new_from_sql(str, 0);

    ok(str, (g != 0));

    assert(g != 0);
    int_is("compare", icalgauge_compare(g, c), 0);

    icalgauge_free(g);

    /* Greater than */

    str = "SELECT * FROM VEVENT WHERE DTSTART > '20000101T000001'";
    g = icalgauge_new_from_sql(str, 0);

    ok(str, (g != 0));

    assert(g != 0);
    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }

    str = "SELECT * FROM VEVENT WHERE DTSTART > '20000101T000002'";
    g = icalgauge_new_from_sql(str, 0);

    ok(str, (g != 0));

    assert(g != 0);
    int_is("compare", icalgauge_compare(g, c), 0);

    icalgauge_free(g);

    /* Greater than or Equal to */

    str = "SELECT * FROM VEVENT WHERE DTSTART >= '20000101T000002'";
    g = icalgauge_new_from_sql(str, 0);

    ok(str, (g != 0));

    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }

    str = "SELECT * FROM VEVENT WHERE DTSTART >= '20000101T000003'";
    g = icalgauge_new_from_sql(str, 0);

    ok(str, (g != 0));

    int_is("compare", icalgauge_compare(g, c), 0);

    icalgauge_free(g);

    /* Less than or Equal to */

    str = "SELECT * FROM VEVENT WHERE DTSTART <= '20000101T000002'";
    g = icalgauge_new_from_sql(str, 0);

    ok(str, (g != 0));

    assert(g != 0);
    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }

    str = "SELECT * FROM VEVENT WHERE DTSTART <= '20000101T000001'";
    g = icalgauge_new_from_sql(str, 0);

    ok(str, (g != 0));

    int_is("compare", icalgauge_compare(g, c), 0);

    icalgauge_free(g);

    icalcomponent_free(c);

    /* Combinations */

    c = icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
            icalcomponent_vanew(
                ICAL_VEVENT_COMPONENT,
                icalproperty_new_dtstart(icaltime_from_string("20000102T000000")), 0),
            (void *)0);

    str = "SELECT * FROM VEVENT WHERE DTSTART > '20000101T000000' and DTSTART < '20000103T000000'";

    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != 0));
    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }

    str = "SELECT * FROM VEVENT WHERE DTSTART > '20000101T000000' and DTSTART < '20000102T000000'";

    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != 0));
    int_is("compare", icalgauge_compare(g, c), 0);

    icalgauge_free(g);

    str = "SELECT * FROM VEVENT WHERE DTSTART > '20000101T000000' or DTSTART < '20000102T000000'";

    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != 0));
    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }

    icalcomponent_free(c);

    /* Combinations, non-cannonical component */

    c = icalcomponent_vanew(ICAL_VEVENT_COMPONENT,
                            icalproperty_new_dtstart(icaltime_from_string("20000102T000000")),
                            (void *)0);

    str = "SELECT * FROM VEVENT WHERE DTSTART > '20000101T000000' and DTSTART < '20000103T000000'";

    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != 0));
    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }

    str = "SELECT * FROM VEVENT WHERE DTSTART > '20000101T000000' and DTSTART < '20000102T000000'";

    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != 0));
    int_is("compare", icalgauge_compare(g, c), 0);

    icalgauge_free(g);

    str = "SELECT * FROM VEVENT WHERE DTSTART > '20000101T000000' or DTSTART < '20000102T000000'";

    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != 0));
    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }
    icalcomponent_free(c);

    /* Complex comparisons */

    c = icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
            icalproperty_new_method(ICAL_METHOD_REQUEST),
            icalcomponent_vanew(
                ICAL_VEVENT_COMPONENT,
                icalproperty_new_dtstart(icaltime_from_string("20000101T000002")),
                icalproperty_new_comment("foo"),
                icalcomponent_vanew(
                    ICAL_VALARM_COMPONENT,
                    icalproperty_new_dtstart(icaltime_from_string("20000101T120000")),
                    (void *)0),
                (void *)0),
            (void *)0);

    str = "SELECT * FROM VEVENT WHERE VALARM.DTSTART = '20000101T120000'";

    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != 0));
    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }

    str = "SELECT * FROM VEVENT WHERE COMMENT = 'foo'";
    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != 0));
    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }

    str = "SELECT * FROM VEVENT WHERE COMMENT = 'foo' AND  VALARM.DTSTART = '20000101T120000'";
    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != 0));
    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }

    str = "SELECT * FROM VEVENT WHERE COMMENT = 'bar' AND  VALARM.DTSTART = '20000101T120000'";
    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != 0));
    int_is("compare", icalgauge_compare(g, c), 0);

    icalgauge_free(g);

    str = "SELECT * FROM VEVENT WHERE COMMENT = 'bar' or  VALARM.DTSTART = '20000101T120000'";
    g = icalgauge_new_from_sql(str, 0);
    ok(str, (g != 0));
    int_is("compare", icalgauge_compare(g, c), 1);

    if (g != NULL) {
        icalgauge_free(g);
    }

    icalcomponent_free(c);
}

icalcomponent *make_component(int i)
{
    icalcomponent *c;

    struct icaltimetype t = icaltime_from_string("20000101T120000Z");

    t.day += i;

    (void)icaltime_normalize(t);

    c = icalcomponent_vanew(ICAL_VCALENDAR_COMPONENT,
                            icalproperty_new_method(ICAL_METHOD_REQUEST),
                            icalcomponent_vanew(ICAL_VEVENT_COMPONENT,
                                                icalproperty_new_dtstart(t), (void *)0), (void *)0);

    assert(c != 0);

    return c;
}

void test_fileset(void)
{
#if defined(HAVE_UNLINK)
    icalset *fs;
    icalcomponent *c;
    int i;
    int comp_count = 0;
    const char *path = "test_fileset.ics";
    icalgauge *g =
        icalgauge_new_from_sql(
            "SELECT * FROM VEVENT "
            "WHERE DTSTART > '20000103T120000Z' AND "
            "DTSTART <= '20000106T120000Z'",
            0);

    ok("icalgauge_new_from_sql()", (g != NULL));

    unlink(path);

    fs = icalfileset_new(path);

    ok("icalfileset_new()", (fs != NULL));
    assert(fs != 0);

    for (i = 0; i != 10; i++) {
        c = make_component(i);
        (void)icalfileset_add_component(fs, c);
    }

    (void)icalfileset_commit(fs);

    icalset_free(fs);
    /** reopen fileset.ics **/
    fs = icalfileset_new(path);

    if (VERBOSE)
        printf("== No Selections \n");

    comp_count = 0;
    for (c = icalfileset_get_first_component(fs); c != 0; c = icalfileset_get_next_component(fs)) {
        struct icaltimetype t = icalcomponent_get_dtstart(c);

        comp_count++;
        if (VERBOSE)
            printf("%s\n", icaltime_as_ctime(t));
    }
    int_is("icalfileset get components", comp_count, 10);

    (void)icalfileset_select(fs, g);

    if (VERBOSE)
        printf("\n== DTSTART > '20000103T120000Z' AND DTSTART <= '20000106T120000Z' \n");

    comp_count = 0;
    for (c = icalfileset_get_first_component(fs); c != 0; c = icalfileset_get_next_component(fs)) {
        struct icaltimetype t = icalcomponent_get_dtstart(c);

        comp_count++;
        if (VERBOSE)
            printf("%s\n", icaltime_as_ctime(t));
    }
    int_is("icalfileset get components with gauge", comp_count, 3);

    icalset_free(fs);

    /*icalgauge_free(g); */
#endif
}

void microsleep(int us)
{       /*us is in microseconds */
#if defined(HAVE_NANOSLEEP)
    struct timespec ts;

    ts.tv_sec = 0;
    ts.tv_nsec = us * 1000;     /* convert from microseconds to nanoseconds */

    nanosleep(&ts, NULL);
#elif defined(HAVE_USLEEP)
    usleep(us);
#endif /*Windows Sleep is useless for microsleeping */
}

void test_file_locks(void)
{
#if defined(HAVE_WAITPID) && defined(HAVE_FORK) && defined(HAVE_UNLINK)
    pid_t pid;
    const char *path = "test_fileset_locktest.ics";
    icalset *fs;
    icalcomponent *c, *c2;
    struct icaldurationtype d;
    int i;
    int final, sec = 0;

    icalerror_clear_errno();

    unlink(path);

    fs = icalfileset_new(path);

    if (icalfileset_get_first_component(fs) == 0) {
        c = make_component(0);

        d = icaldurationtype_from_int(1);

        icalcomponent_set_duration(c, d);

        (void)icalfileset_add_component(fs, c);

        c2 = icalcomponent_new_clone(c);

        (void)icalfileset_add_component(fs, c2);

        (void)icalfileset_commit(fs);
    }

    icalset_free(fs);

    assert(icalerrno == ICAL_NO_ERROR);

    pid = fork();

    assert(pid >= 0);

    if (pid == 0) {
        /*child */
        int i;

        microsleep(rand() / (RAND_MAX / 100));

        for (i = 0; i < 50; i++) {
            fs = icalfileset_new(path);

            assert(fs != 0);

            c = icalfileset_get_first_component(fs);

            assert(c != 0);

            d = icalcomponent_get_duration(c);
            d = icaldurationtype_from_int(icaldurationtype_as_int(d) + 1);

            icalcomponent_set_duration(c, d);
            icalcomponent_set_summary(c, "Child");

            c2 = icalcomponent_new_clone(c);
            icalcomponent_set_summary(c2, "Child");
            (void)icalfileset_add_component(fs, c2);

            icalfileset_mark(fs);
            (void)icalfileset_commit(fs);

            icalset_free(fs);

            microsleep(rand() / (RAND_MAX / 20));
        }

        exit(0);

    } else {
        /* parent */
        int i;

        for (i = 0; i < 50; i++) {
            fs = icalfileset_new(path);

            assert(fs != 0);

            c = icalfileset_get_first_component(fs);

            assert(c != 0);

            d = icalcomponent_get_duration(c);
            d = icaldurationtype_from_int(icaldurationtype_as_int(d) + 1);

            icalcomponent_set_duration(c, d);
            icalcomponent_set_summary(c, "Parent");

            c2 = icalcomponent_new_clone(c);
            icalcomponent_set_summary(c2, "Parent");
            (void)icalfileset_add_component(fs, c2);

            icalfileset_mark(fs);
            (void)icalfileset_commit(fs);
            icalset_free(fs);

            putc('.', stdout);
            fflush(stdout);
        }
    }

    assert(waitpid(pid, 0, 0) == pid);

    fs = icalfileset_new(path);

    i = 1;

    c = icalfileset_get_first_component(fs);
    final = icaldurationtype_as_int(icalcomponent_get_duration(c));
    for (c = icalfileset_get_next_component(fs); c != 0; c = icalfileset_get_next_component(fs)) {
        struct icaldurationtype d = icalcomponent_get_duration(c);

        sec = icaldurationtype_as_int(d);

        /*printf("%d,%d ",i,sec); */
        assert(i == sec);
        i++;
    }

    printf("\nFinal: %d\n", final);

    assert(sec == final);
    _unused(sec);
#endif
}

void test_action(void)
{
    icalcomponent *c;
    icalproperty *p;
    char *str;

    static const char test_icalcomp_str[] =
        "BEGIN:VEVENT\r\n"
        "ACTION:EMAIL\r\n"
        "ACTION:PROCEDURE\r\n" "ACTION:AUDIO\r\n" "ACTION:FUBAR\r\n" "END:VEVENT\r\n";

    c = icalparser_parse_string((char *)test_icalcomp_str);

    ok("icalparser_parse_string(), ACTIONS", (c != NULL));
    assert(c != 0);

    str = icalcomponent_as_ical_string(c);
    str_is("icalcomponent_as_ical_string()", str, ((char *)test_icalcomp_str));
    if (VERBOSE)
        printf("%s\n\n", str);

    p = icalcomponent_get_first_property(c, ICAL_ACTION_PROPERTY);

    ok("ICAL_ACTION_EMAIL", (icalproperty_get_action(p) == ICAL_ACTION_EMAIL));

    p = icalcomponent_get_next_property(c, ICAL_ACTION_PROPERTY);

    ok("ICAL_ACTION_PROCEDURE", (icalproperty_get_action(p) == ICAL_ACTION_PROCEDURE));

    p = icalcomponent_get_next_property(c, ICAL_ACTION_PROPERTY);

    ok("ICAL_ACTION_AUDIO", (icalproperty_get_action(p) == ICAL_ACTION_AUDIO));

    p = icalcomponent_get_next_property(c, ICAL_ACTION_PROPERTY);

    ok("ICAL_ACTION_X", (icalproperty_get_action(p) == ICAL_ACTION_X));
    str_is("ICAL_ACTION -> FUBAR", icalvalue_get_x(icalproperty_get_value(p)), "FUBAR");
    icalcomponent_free(c);
}

void test_trigger(void)
{
    struct icaltriggertype tr;
    icalcomponent *c;
    icalproperty *p;
    const char *str;

    static const char test_icalcomp_str[] =
        "BEGIN:VEVENT\r\n"
        "TRIGGER;VALUE=DATE-TIME:19980403T120000\r\n"
        "TRIGGER:-PT15M\r\n"
        "TRIGGER;VALUE=DATE-TIME:19980403T120000\r\n"
#ifdef NEEDS_REVIEW /* latest output code will suppress VALUE= for default type */
        "TRIGGER;VALUE=DURATION:-PT15M\r\n"
#endif
        "END:VEVENT\r\n";

    c = icalparser_parse_string((char *)test_icalcomp_str);
    ok("icalparser_parse_string()", (c != NULL));
    assert(c != NULL);

    str_is("parsed triggers", icalcomponent_as_ical_string(c), (char *)test_icalcomp_str);

    for (p = icalcomponent_get_first_property(c, ICAL_TRIGGER_PROPERTY);
         p != 0; p = icalcomponent_get_next_property(c, ICAL_TRIGGER_PROPERTY)) {
        tr = icalproperty_get_trigger(p);

        if (!icaltime_is_null_time(tr.time)) {
            if (VERBOSE)
                printf("value=DATE-TIME:%s\n", icaltime_as_ical_string(tr.time));
        } else {
            if (VERBOSE)
                printf("value=DURATION:%s\n", icaldurationtype_as_ical_string(tr.duration));
        }
    }

    icalcomponent_free(c);

    /* Trigger, as a DATETIME */
    tr.duration = icaldurationtype_null_duration();
    tr.time = icaltime_from_string("19970101T120000");
    p = icalproperty_new_trigger(tr);
    str = icalproperty_as_ical_string(p);

    str_is("TRIGGER;VALUE=DATE-TIME:19970101T120000", str,
           "TRIGGER;VALUE=DATE-TIME:19970101T120000\r\n");
    icalproperty_free(p);

    /* TRIGGER, as a DURATION */
    tr.time = icaltime_null_time();
    tr.duration = icaldurationtype_from_string("P3DT3H50M45S");
    p = icalproperty_new_trigger(tr);
    str = icalproperty_as_ical_string(p);

    str_is("TRIGGER:P3DT3H50M45S", str, "TRIGGER:P3DT3H50M45S\r\n");
    icalproperty_free(p);

    /* TRIGGER, as a DATETIME, VALUE=DATETIME */
    tr.duration = icaldurationtype_null_duration();
    tr.time = icaltime_from_string("19970101T120000");
    p = icalproperty_new_trigger(tr);
    icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_DATETIME));
    str = icalproperty_as_ical_string(p);

    str_is("TRIGGER;VALUE=DATE-TIME:19970101T120000", str,
           "TRIGGER;VALUE=DATE-TIME:19970101T120000\r\n");
    icalproperty_free(p);
#ifdef NEEDS_REVIEW /* latest output code will suppress VALUE= for default type */
    /*TRIGGER, as a DURATION, VALUE=DURATION */
    tr.time = icaltime_null_time();
    tr.duration = icaldurationtype_from_string("P3DT3H50M45S");
    p = icalproperty_new_trigger(tr);
    icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_DURATION));

    str = icalproperty_as_ical_string(p);

    str_is("TRIGGER;VALUE=DURATION:P3DT3H50M45S", str, "TRIGGER;VALUE=DURATION:P3DT3H50M45S\r\n");
    icalproperty_free(p);
#endif
    /* TRIGGER, as a DATETIME, VALUE=DURATION */
    tr.duration = icaldurationtype_null_duration();
    tr.time = icaltime_from_string("19970101T120000");
    p = icalproperty_new_trigger(tr);
    icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_DATETIME));
    str = icalproperty_as_ical_string(p);

    str_is("TRIGGER;VALUE=DATE-TIME:19970101T120000", str,
           "TRIGGER;VALUE=DATE-TIME:19970101T120000\r\n");
    icalproperty_free(p);
#ifdef NEEDS_REVIEW /* latest output code will suppress VALUE= for default type */
    /*TRIGGER, as a DURATION, VALUE=DURATION */
    tr.time = icaltime_null_time();
    tr.duration = icaldurationtype_from_string("P3DT3H50M45S");
    p = icalproperty_new_trigger(tr);
    icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_DURATION));

    str = icalproperty_as_ical_string(p);

    str_is("TRIGGER;VALUE=DURATION:P3DT3H50M45S", str, "TRIGGER;VALUE=DURATION:P3DT3H50M45S\r\n");
    icalproperty_free(p);
#endif
    /* TRIGGER, as a DATETIME, VALUE=BINARY  */
    tr.duration = icaldurationtype_null_duration();
    tr.time = icaltime_from_string("19970101T120000");
    p = icalproperty_new_trigger(tr);
    icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_BINARY));
    str = icalproperty_as_ical_string(p);

    str_is("TRIGGER;VALUE=BINARY:19970101T120000", str, "TRIGGER;VALUE=BINARY:19970101T120000\r\n");
    icalproperty_free(p);

    /*TRIGGER, as a DURATION, VALUE=BINARY   */
    tr.time = icaltime_null_time();
    tr.duration = icaldurationtype_from_string("P3DT3H50M45S");
    p = icalproperty_new_trigger(tr);
    icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_BINARY));

    str = icalproperty_as_ical_string(p);

    str_is("TRIGGER;VALUE=BINARY:P3DT3H50M45S", str, "TRIGGER;VALUE=BINARY:P3DT3H50M45S\r\n");
    icalproperty_free(p);
}

void test_rdate(void)
{
    struct icaldatetimeperiodtype dtp;
    icalproperty *p;
    const char *str;
    struct icalperiodtype period;

    period.start = icaltime_from_string("19970101T120000");
    period.end = icaltime_null_time();
    period.duration = icaldurationtype_from_string("PT3H10M15S");

    /* RDATE, as DATE-TIME */
    dtp.time = icaltime_from_string("19970101T120000");
    dtp.period = icalperiodtype_null_period();
    p = icalproperty_new_rdate(dtp);
    str = icalproperty_as_ical_string(p);

    str_is("RDATE as DATE-TIME", str, "RDATE:19970101T120000\r\n");
    icalproperty_free(p);

    /* RDATE, as PERIOD */
    dtp.time = icaltime_null_time();
    dtp.period = period;
    p = icalproperty_new_rdate(dtp);

    str = icalproperty_as_ical_string(p);
    str_is("RDATE, as PERIOD", str, "RDATE;VALUE=PERIOD:19970101T120000/PT3H10M15S\r\n");
    icalproperty_free(p);
#ifdef NEEDS_REVIEW /* latest output code will suppress VALUE= for default type */
    /* RDATE, as DATE-TIME, VALUE=DATE-TIME */
    dtp.time = icaltime_from_string("19970101T120000");
    dtp.period = icalperiodtype_null_period();
    p = icalproperty_new_rdate(dtp);
    icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_DATETIME));
    str = icalproperty_as_ical_string(p);

    str_is("RDATE, as DATE-TIME, VALUE=DATE-TIME", str,
           "RDATE;VALUE=DATE-TIME:19970101T120000\r\n");
    icalproperty_free(p);
#endif
    /* RDATE, as PERIOD, VALUE=DATE-TIME */
    dtp.time = icaltime_null_time();
    dtp.period = period;
    p = icalproperty_new_rdate(dtp);
    icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_PERIOD));
    str = icalproperty_as_ical_string(p);
    str_is("RDATE, as PERIOD, VALUE=DATE-TIME", str,
           "RDATE;VALUE=PERIOD:19970101T120000/PT3H10M15S\r\n");
    icalproperty_free(p);
#ifdef NEEDS_REVIEW /* latest output code will suppress VALUE= for default type */
    /* RDATE, as DATE-TIME, VALUE=PERIOD */
    dtp.time = icaltime_from_string("19970101T120000");
    dtp.period = icalperiodtype_null_period();
    p = icalproperty_new_rdate(dtp);
    icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_DATETIME));
    str = icalproperty_as_ical_string(p);

    str_is("RDATE, as DATE-TIME, VALUE=PERIOD", str, "RDATE;VALUE=DATE-TIME:19970101T120000\r\n");
    icalproperty_free(p);
#endif
    /* RDATE, as PERIOD, VALUE=PERIOD */
    dtp.time = icaltime_null_time();
    dtp.period = period;
    p = icalproperty_new_rdate(dtp);
    icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_PERIOD));
    str = icalproperty_as_ical_string(p);

    str_is("RDATE, as PERIOD, VALUE=PERIOD", str,
           "RDATE;VALUE=PERIOD:19970101T120000/PT3H10M15S\r\n");
    icalproperty_free(p);

    /* RDATE, as DATE-TIME, VALUE=BINARY */
    dtp.time = icaltime_from_string("19970101T120000");
    dtp.period = icalperiodtype_null_period();
    p = icalproperty_new_rdate(dtp);
    icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_BINARY));
    str = icalproperty_as_ical_string(p);

    str_is("RDATE, as DATE-TIME, VALUE=BINARY", str, "RDATE;VALUE=BINARY:19970101T120000\r\n");
    icalproperty_free(p);

    /* RDATE, as PERIOD, VALUE=BINARY */
    dtp.time = icaltime_null_time();
    dtp.period = period;
    p = icalproperty_new_rdate(dtp);
    icalproperty_add_parameter(p, icalparameter_new_value(ICAL_VALUE_BINARY));
    str = icalproperty_as_ical_string(p);

    str_is("RDATE, as PERIOD, VALUE=BINARY", str,
           "RDATE;VALUE=BINARY:19970101T120000/PT3H10M15S\r\n");
    icalproperty_free(p);
}

void test_langbind(void)
{
    icalcomponent *c, *inner;
    icalproperty *p;
    char *test_str_parsed;
    static const char test_str[] =
        "BEGIN:VEVENT\n"
        "ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP:MAILTO:employee-A@host.com\n"
        "COMMENT: Comment that \n spans a line\n"
        "COMMENT: Comment with \"quotable\" \'characters\' and other \t bad magic \n things \f Yeah.\n"
        "DTSTART:19970101T120000\n"
        "DTSTART:19970101T120000Z\n"
        "DTSTART:19970101\n"
        "DURATION:P3DT4H25M\n"
        "FREEBUSY:19970101T120000/19970101T120000\n"
        "FREEBUSY:19970101T120000/P3DT4H25M\n" "END:VEVENT\n";

    static const char *test_str_parsed_good =
        "BEGIN:VEVENT\r\n"
        "ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP:MAILTO:\r\n"
        " employee-A@host.com\r\n"
        "COMMENT:Comment that spans a line\r\n"
        "COMMENT:Comment with \"quotable\" 'characters' and other 	 bad magic things \r\n"
        "  Yeah.\r\n"
        "DTSTART:19970101T120000\r\n"
        "DTSTART:19970101T120000Z\r\n"
        "DTSTART;VALUE=DATE:19970101\r\n"
        "DURATION:P3DT4H25M\r\n"
        "FREEBUSY:19970101T120000/19970101T120000\r\n"
        "FREEBUSY:19970101T120000/P3DT4H25M\r\n" "END:VEVENT\r\n";

    if (VERBOSE)
        printf("%s\n", test_str);

    c = icalparser_parse_string(test_str);

    ok("icalparser_parse_string()", (c != NULL));
    assert(c != NULL);

    test_str_parsed = icalcomponent_as_ical_string(c);

    str_is("parsed version with bad chars, etc", test_str_parsed, test_str_parsed_good);

    inner = icalcomponent_get_inner(c);

    for (p = icallangbind_get_first_property(inner, "ANY");
         p != 0; p = icallangbind_get_next_property(inner, "ANY")
        ) {

        const char *str = icallangbind_property_eval_string(p, ":");

      /** TODO add tests **/
        if (VERBOSE)
            printf("%s\n", str);
    }

    p = icalcomponent_get_first_property(inner, ICAL_ATTENDEE_PROPERTY);

    icalproperty_set_parameter_from_string(p, "CUTYPE", "INDIVIDUAL");

    str_is("Set attendee parameter",
           icalproperty_as_ical_string(p),
           "ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=INDIVIDUAL:MAILTO:\r\n"
           " employee-A@host.com\r\n");

    icalproperty_set_value_from_string(p, "mary@foo.org", "TEXT");

    str_is("Set attendee parameter value",
           icalproperty_as_ical_string(p),
           "ATTENDEE;VALUE=TEXT;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=INDIVIDUAL:\r\n"
           " mary@foo.org\r\n");

    icalcomponent_free(c);
}

void test_property_parse(void)
{
    icalcomponent *c;
    icalproperty *p;
    const char *str;

    static const char test_icalcomp_str[] =
        "BEGIN:VEVENT\n"
        "ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP:MAILTO:employee-A@host.com\n"
        "DTSTART:19970101T120000Z\n" "END:VEVENT\n";

    c = icalparser_parse_string((char *)test_icalcomp_str);
    ok("icalparser_parse_string()", (c != NULL));
    if (!c) {
        exit(EXIT_FAILURE);
    }

    p = icalcomponent_get_first_property(c, ICAL_ATTENDEE_PROPERTY);

    ok("icalproperty_from_string(), ATTENDEE", (p != 0));

    str = icalproperty_as_ical_string(p);
    if (VERBOSE)
        printf("%s\n", str);

    icalproperty_free(p);

    p = icalcomponent_get_first_property(c, ICAL_DTSTART_PROPERTY);

    ok("icalproperty_from_string(), simple DTSTART", (p != 0));

    str = icalproperty_as_ical_string(p);
    if (VERBOSE)
        printf("%s\n", str);

    icalproperty_free(p);
    icalcomponent_free(c);
}

void test_value_parameter(void)
{
    icalcomponent *c;
    icalproperty *p;
    icalparameter *param;

    static const char test_icalcomp_str[] =
        "BEGIN:VEVENT\n"
        "DTSTART;VALUE=DATE-TIME:19971123T123000\n"
        "DTSTART;VALUE=DATE:19971123\n" "DTSTART;VALUE=FOO:19971123T123000\n" "END:VEVENT\n";

    c = icalparser_parse_string((char *)test_icalcomp_str);
    ok("icalparser_parse_string()", (c != NULL));
    if (!c) {
        exit(EXIT_FAILURE);
    }

    if (VERBOSE)
        printf("%s", icalcomponent_as_ical_string(c));

    p = icalcomponent_get_first_property(c, ICAL_DTSTART_PROPERTY);
    param = icalproperty_get_first_parameter(p, ICAL_VALUE_PARAMETER);

    ok("icalproperty_get_value()", (icalparameter_get_value(param) == ICAL_VALUE_DATETIME));

    p = icalcomponent_get_next_property(c, ICAL_DTSTART_PROPERTY);
    param = icalproperty_get_first_parameter(p, ICAL_VALUE_PARAMETER);
    ok("icalproperty_get_first_parameter()", (icalparameter_get_value(param) == ICAL_VALUE_DATE));

    icalcomponent_free(c);
}

void test_empty_parameter(void)
{
    icalcomponent *c;
    icalproperty *p;
    icalparameter *param;

    static const char test_icalcomp_str[] =
        "BEGIN:VEVENT\n"
        "ATTENDEE;CN=\"\";RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP:MAILTO:employee-A@host.com\n"
        "END:VEVENT\n";

    c = icalparser_parse_string((char *)test_icalcomp_str);
    ok("icalparser_parse_string()", (c != NULL));
    if (!c) {
        exit(EXIT_FAILURE);
    }

    if (VERBOSE)
        printf("%s", icalcomponent_as_ical_string(c));

    p = icalcomponent_get_first_property(c, ICAL_ATTENDEE_PROPERTY);
    param = icalproperty_get_first_parameter(p, ICAL_CN_PARAMETER);

    ok("icalparameter_get_cn()", (0 == strcmp("", icalparameter_get_cn(param))));

    icalcomponent_free(c);
}

void test_x_parameter(void)
{
    icalcomponent *c;
    icalproperty *p;

    static const char test_icalcomp_str[] =
        "BEGIN:VEVENT\n" "COMMENT;X-A=1;X-B=2:\\sThis is a note\n" "END:VEVENT\n";

    c = icalparser_parse_string((char *)test_icalcomp_str);
    ok("icalparser_parse_string()", (c != NULL));
    if (!c) {
        exit(EXIT_FAILURE);
    }

    if (VERBOSE)
        printf("%s", icalcomponent_as_ical_string(c));

    p = icalcomponent_get_first_property(c, ICAL_COMMENT_PROPERTY);
    icalproperty_set_parameter_from_string(p, "X-LIES", "no");
    icalproperty_set_parameter_from_string(p, "X-LAUGHS", "big");
    icalproperty_set_parameter_from_string(p, "X-TRUTH", "yes");
    icalproperty_set_parameter_from_string(p, "X-HUMOUR", "bad");

    if (VERBOSE)
        printf("%s\n", icalproperty_as_ical_string(p));

    str_is("COMMENT parses param", icalproperty_get_comment(p), " This is a note");

    str_is("Check X-LIES", icalproperty_get_parameter_as_string(p, "X-LIES"), "no");
    str_is("Check X-LAUGHS", icalproperty_get_parameter_as_string(p, "X-LAUGHS"), "big");
    str_is("Check X-TRUTH", icalproperty_get_parameter_as_string(p, "X-TRUTH"), "yes");
    str_is("Check X-HUMOUR", icalproperty_get_parameter_as_string(p, "X-HUMOUR"), "bad");

    icalcomponent_free(c);
}

void test_x_property(void)
{
    icalcomponent *c;
    icalproperty *p;

    static const char test_icalcomp_str[] =
        "BEGIN:VEVENT\n" "X-LIC-PROPERTY:\\sThis is a note\n" "END:VEVENT\n";

    c = icalparser_parse_string((char *)test_icalcomp_str);
    ok("icalparser_parse_string()", (c != NULL));
    if (!c) {
        exit(EXIT_FAILURE);
    }

    if (VERBOSE)
        printf("%s", icalcomponent_as_ical_string(c));

    p = icalcomponent_get_first_property(c, ICAL_X_PROPERTY);
    ok("x-property is correct kind", (icalproperty_isa(p) == ICAL_X_PROPERTY));
    str_is("icalproperty_get_x_name() works", icalproperty_get_x_name(p), "X-LIC-PROPERTY");
    str_is("icalproperty_get_x() works", icalproperty_get_x(p), " This is a note");

    icalcomponent_free(c);
}

void test_utcoffset(void)
{
    icalcomponent *c;

    static const char test_icalcomp_str[] =
        "BEGIN:VTIMEZONE\n" "TZOFFSETFROM:-001608\n" "END:VTIMEZONE\n";

    c = icalparser_parse_string((char *)test_icalcomp_str);
    ok("parse TZOFFSETFROM:-001608", (c != NULL));

    if (VERBOSE && c)
        printf("%s", icalcomponent_as_ical_string(c));

    if (c)
        icalcomponent_free(c);
}

void test_attach(void)
{
    icalcomponent *c;

    static const char test_icalcomp_str[] =
        "BEGIN:VEVENT\n"
        "ATTACH:CID:jsmith.part3.960817T083000.xyzMain@host1.com\n"
        "ATTACH:FMTTYPE=application/postscript;ftp://xyzCorp.com/pub/reports/r-960812.ps\n"
        "END:VEVENT\n";

    c = icalparser_parse_string((char *)test_icalcomp_str);
    ok("parse simple attachment", (c != NULL));

    if (VERBOSE && c)
        printf("%s", icalcomponent_as_ical_string(c));

    if (c)
        icalcomponent_free(c);
}

void test_attach_caldav(void)
{
    icalcomponent *c;
    icalproperty *p;
    icalparameter *param;

    static const char test_icalcomp_str_caldav[] =
        "BEGIN:VEVENT\n"
        "ATTACH;MANAGED-ID=e474a36f22;FMTTYPE=image/jpeg;SIZE=281639;FILENAME=2doimage.jpg:https://www.someurl.com/somefile.jpg\n"
        "END:VEVENT\n";

    c = icalparser_parse_string((char *)test_icalcomp_str_caldav);
    ok("parse caldav attachment", (c != NULL));

    if (VERBOSE && c)
        printf("%s", icalcomponent_as_ical_string(c));

    p = icalcomponent_get_first_property(c, ICAL_ATTACH_PROPERTY);
    ok("property is correct kind (attach)", (icalproperty_isa(p) == ICAL_ATTACH_PROPERTY));

    param = icalproperty_get_first_parameter(p, ICAL_ANY_PARAMETER);
    if (VERBOSE && param) {
        printf("MANAGED-ID = %s\n", icalparameter_get_managedid(param));
    }
    str_is("managed-id", icalparameter_get_managedid(param), "e474a36f22");

    param = icalproperty_get_next_parameter(p, ICAL_ANY_PARAMETER);
    if (VERBOSE && param) {
        printf("FMTTYPE = %s\n", icalparameter_get_fmttype(param));
    }
    str_is("fmttype", icalparameter_get_fmttype(param), "image/jpeg");

    param = icalproperty_get_next_parameter(p, ICAL_ANY_PARAMETER);
    if (VERBOSE && param) {
        printf("SIZE = %s\n", icalparameter_get_size(param));
    }
    str_is("size", icalparameter_get_size(param), "281639");

    param = icalproperty_get_next_parameter(p, ICAL_ANY_PARAMETER);
    if (VERBOSE && param) {
        printf("FILENAME = %s\n", icalparameter_get_filename(param));
    }
    str_is("filename", icalparameter_get_filename(param), "2doimage.jpg");

    if (VERBOSE) {
        printf("URI = %s\n", icalattach_get_url(icalproperty_get_attach(p)));
    }
    str_is("attach url", icalattach_get_url(icalproperty_get_attach(p)),
           "https://www.someurl.com/somefile.jpg");

    if (p)
        icalproperty_free(p);

    if (c)
        icalcomponent_free(c);
}

void test_attach_url(void)
{
    static const char test_icalcomp_str_attachwithurl[] =
        "BEGIN:VALARM\r\n" "ATTACH:foofile\r\n" "END:VALARM\r\n";

    icalattach *attach = icalattach_new_from_url("foofile");
    icalcomponent *ac = icalcomponent_new(ICAL_VALARM_COMPONENT);
    icalproperty *ap = icalproperty_new_attach(attach);

    icalcomponent_add_property(ac, ap);
    if (VERBOSE) {
        printf("%s\n", icalcomponent_as_ical_string(ac));
    }
    str_is("attach url", icalattach_get_url(attach), "foofile");
    str_is("attach with url", icalcomponent_as_ical_string(ac), test_icalcomp_str_attachwithurl);
    icalattach_unref(attach);
    icalproperty_free(ap);
    icalcomponent_free(ac);
}

static void test_free_attach_data(char *data, void *user_data)
{
    int *pbeen_called = (int *) user_data;

    free(data);

    (*pbeen_called)++;
}

void test_attach_data(void)
{
    static const char test_icalcomp_str_attachwithdata[] =
        "BEGIN:VALARM\r\n" "ATTACH;VALUE=BINARY:foofile\r\n" "END:VALARM\r\n";
    static const char test_icalcomp_str_attachwithencodingdata[] =
        "BEGIN:VALARM\r\n" "ATTACH;VALUE=BINARY;ENCODING=BASE64:YQECAAACAWEK\r\n" "END:VALARM\r\n";

    int free_been_called = 0;
    icalattach *attach = icalattach_new_from_data("foofile", NULL, 0);
    icalcomponent *ac = icalcomponent_new(ICAL_VALARM_COMPONENT);
    icalproperty *ap = icalproperty_new_attach(attach);

    icalcomponent_add_property(ac, ap);
    if (VERBOSE) {
        printf("%s\n", icalcomponent_as_ical_string(ac));
    }
    str_is("attach data", (const char *) icalattach_get_data(attach), "foofile");
    str_is("attach with data", icalcomponent_as_ical_string(ac), test_icalcomp_str_attachwithdata);

    icalattach_unref(attach);
    icalcomponent_free(ac);

    ac = icalcomponent_new_from_string(test_icalcomp_str_attachwithdata);
    ap = icalcomponent_get_first_property(ac, ICAL_ATTACH_PROPERTY);
    attach = icalproperty_get_attach(ap);
    str_is("attach data 2", (const char *) icalattach_get_data(attach), "foofile");
    str_is("attach with data 2", icalcomponent_as_ical_string(ac), test_icalcomp_str_attachwithdata);

    icalcomponent_free(ac);

    attach = icalattach_new_from_data(strdup("foofile"), test_free_attach_data, &free_been_called);
    ac = icalcomponent_new(ICAL_VALARM_COMPONENT);
    ap = icalproperty_new_attach(attach);

    icalcomponent_add_property(ac, ap);
    if (VERBOSE) {
        printf("%s\n", icalcomponent_as_ical_string(ac));
    }
    str_is("attach data 3", (const char *) icalattach_get_data(attach), "foofile");
    str_is("attach with data 3", icalcomponent_as_ical_string(ac), test_icalcomp_str_attachwithdata);

    icalattach_unref(attach);
    ok("Free should not be called yet", (!free_been_called));
    icalcomponent_free(ac);
    ok("Free should be called now", (free_been_called == 1));

    ac = icalcomponent_new_from_string(test_icalcomp_str_attachwithencodingdata);
    ap = icalcomponent_get_first_property(ac, ICAL_ATTACH_PROPERTY);
    attach = icalproperty_get_attach(ap);
    str_is("attach data 4", (const char *) icalattach_get_data(attach), "YQECAAACAWEK");
    str_is("attach with data 4", icalcomponent_as_ical_string(ac), test_icalcomp_str_attachwithencodingdata);

    icalcomponent_free(ac);
}

void test_vcal(void)
{
    VObject *vcal;
    icalcomponent *comp;
    const char *file = TEST_DATADIR "/user-cal.vcf";

    vcal = Parse_MIME_FromFileName(file);

    ok("Parsing " TEST_DATADIR "/user-cal.vcf", (vcal != 0));

    if (vcal) {
        comp = icalvcal_convert(vcal);

        ok("Converting to ical component", (comp != 0));

        if (VERBOSE && comp) {
            printf("%s\n", icalcomponent_as_ical_string(comp));
        }

        if (comp) {
            icalcomponent_free(comp);
        }

        cleanVObject(vcal);
    }
}

/*
 * Test to see if recurrences are excluded in certain situations
 * See r961 for more information
 */
void test_recurrenceexcluded(void)
{
    char funTime[2048];
    icalcomponent *calendar = NULL;
    icalcomponent *event = NULL;
    struct icaltimetype dtstart;
    struct icaltimetype recurtime;

    funTime[0] = '\0';
    strcat(funTime, "BEGIN:VCALENDAR\n");
    strcat(funTime, "VERSION:2.0\n");
    strcat(funTime, "BEGIN:VTIMEZONE\n");
    strcat(funTime, "TZID:/mozilla.org/20071231_1/Europe/London\n");
    strcat(funTime, "X-LIC-LOCATION:Europe/London\n");
    strcat(funTime, "BEGIN:DAYLIGHT\n");
    strcat(funTime, "TZOFFSETFROM:+0000\n");
    strcat(funTime, "TZOFFSETTO:+0100\n");
    strcat(funTime, "TZNAME:BST\n");
    strcat(funTime, "DTSTART:19700328T230000\n");
    strcat(funTime, "RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3\n");
    strcat(funTime, "END:DAYLIGHT\n");
    strcat(funTime, "BEGIN:STANDARD\n");
    strcat(funTime, "TZOFFSETFROM:+0100\n");
    strcat(funTime, "TZOFFSETTO:+0000\n");
    strcat(funTime, "TZNAME:GMT\n");
    strcat(funTime, "DTSTART:19701025T000000\n");
    strcat(funTime, "RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10\n");
    strcat(funTime, "END:STANDARD\n");
    strcat(funTime, "END:VTIMEZONE\n");
    strcat(funTime, "BEGIN:VEVENT\n");
    strcat(funTime, "DTSTAMP:20080805T174443Z\n");
    strcat(funTime, "UID:5fb6ccb8-9646-45ab-8c95-8d15e9de1280\n");
    strcat(funTime, "SUMMARY:Exclude test\n");
    strcat(funTime, "EXDATE;TZID=/mozilla.org/20071231_1/Europe/London:20080818T190000\n");
    strcat(funTime, "EXDATE:20080819T180000Z\n");
    strcat(funTime, "RRULE:FREQ=DAILY;COUNT=12;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR\n");
    strcat(funTime, "DTSTART;TZID=/mozilla.org/20071231_1/Europe/London:20080811T190000\n");
    strcat(funTime, "DTEND;TZID=/mozilla.org/20071231_1/Europe/London:20080811T200000\n");
    strcat(funTime, "END:VEVENT\n");
    strcat(funTime, "END:VCALENDAR\n");
    calendar = icalparser_parse_string(funTime);
    dtstart = icalcomponent_get_dtstart(calendar);
    event = icalcomponent_get_first_component(calendar, ICAL_VEVENT_COMPONENT);
    recurtime = icaltime_from_string("20080818T180000Z");
    ok("Recurrence is excluded as per r961",
       icalproperty_recurrence_is_excluded(event, &dtstart, &recurtime));
    recurtime = icaltime_from_string("20080819T180000Z");
    ok("Recurrence is excluded for UTC EXDATE",
       icalproperty_recurrence_is_excluded(event, &dtstart, &recurtime));
    icalcomponent_free(calendar);
}

void test_bad_dtstart_in_timezone(void)
{
    icaltimezone *myTZ = NULL;
    icalcomponent *vtimezone = NULL;
    char *str = NULL;

    myTZ = icaltimezone_get_builtin_timezone("Europe/Zurich");
    vtimezone = icaltimezone_get_component(myTZ);
    str = icalcomponent_as_ical_string(vtimezone);

    if (VERBOSE)
        printf("%s\n", str);

    ok("bad-dtstart-in-timezone.patch r960", (strstr(str, "DTSTART:20371025T030000") != NULL));
    ok("bad-dtstart-in-timezone.patch r960", (strstr(str, "DTSTART:20371025T030000") != NULL));
}

void test_icalcomponent_new_from_string(void)
{
    const char *item =
        "BEGIN:VCALENDAR\n"
        "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
        "VERSION:2.0\n"
        "BEGIN:VEVENT\n"
        "SUMMARY:phone meeting\n"
        "DTEND:20060406T163000Z\n"
        "DTSTART:20060406T160000Z\n"
        "UID:1234567890@dummy\n"
        "DTSTAMP:20110824T104144Z\n"
        "LAST-MODIFIED:20110824T104144Z\n"
        "CREATED:20060409T213201\n"
        "LOCATION:my office\n"
        "DESCRIPTION:let's talk\n"
        "CLASS:PUBLIC\n" "TRANSP:OPAQUE\n" "SEQUENCE:1\n" "END:VEVENT\n" "END:VCALENDAR\n";
    // must succeed and not leak memory...
    icalcomponent *comp = icalcomponent_new_from_string(item);

    ok("parsed", (comp != NULL));
    icalcomponent_free(comp);
}

void test_comma_in_quoted_value(void)
{
    icalcomponent *c;
    icalproperty *p;

    static const char test_icalcomp_str[] =
        "BEGIN:VEVENT\n" "X-TEST;VALUE=URI:\"geo:10.123456,-70.123456\"\n" "END:VEVENT\n";

    c = icalparser_parse_string((char *)test_icalcomp_str);
    ok("icalparser_parse_string()", (c != NULL));
    if (!c) {
        exit(EXIT_FAILURE);
    }

    if (VERBOSE)
        printf("%s", icalcomponent_as_ical_string(c));

    p = icalcomponent_get_first_property(c, ICAL_X_PROPERTY);
    ok("x-property is correct kind", (icalproperty_isa(p) == ICAL_X_PROPERTY));
    str_is("icalproperty_get_x_name() works", icalproperty_get_x_name(p), "X-TEST");
    str_is("icalproperty_get_value_as_string() works",
           icalproperty_get_value_as_string(p), "\"geo:10.123456,-70.123456\"");

    icalcomponent_free(c);
}

void test_tzid_with_utc_time(void)
{
    const char *strcomp =
        "BEGIN:VCALENDAR\r\n"
        "BEGIN:VTIMEZONE\r\n"
        "TZID:my_zone\r\n"
        "BEGIN:STANDARD\r\n"
        "TZNAME:my_zone\r\n"
        "DTSTART:19160429T230000\r\n"
        "TZOFFSETFROM:+0100\r\n"
        "TZOFFSETTO:+0200\r\n"
        "RRULE:FREQ=YEARLY;UNTIL=19160430T220000Z;BYDAY=-1SU;BYMONTH=4\r\n"
        "END:STANDARD\r\n"
        "END:VTIMEZONE\r\n"
        "BEGIN:VEVENT\r\n"
        "UID:0\r\n"
        "DTSTART;TZID=my_zone:20180101T010000Z\r\n"
        "DTEND;TZID=my_zone:20180101T030000\r\n"
        "DUE:20180101T030000Z\r\n"
        "RRULE:FREQ=HOURLY;UNTIL=20180505T050000Z;BYHOUR=1\r\n"
        "EXDATE:20180101T010000\r\n"
        "EXDATE:20180202T020000Z\r\n"
        "EXDATE;TZID=my_zone:20180303T030000\r\n"
        "EXDATE;TZID=my_zone:20180404T040000Z\r\n"
        "END:VEVENT\r\n"
        "END:VCALENDAR\r\n";
    struct _exdate_expected {
        const char *tzid;
    } exdate_expected[] = {
        { "" },
        { "UTC" },
        { "my_zone" },
        { "UTC" },
        { NULL }
    };
    icalcomponent *comp, *subcomp;
    struct icaltimetype dtstart, dtend, due;
    icalproperty *prop;
    int idx;

    comp = icalcomponent_new_from_string(strcomp);
    ok("icalcomponent_new_from_string()", (comp != NULL));
    subcomp = icalcomponent_get_first_component(comp, ICAL_VEVENT_COMPONENT);
    ok("get subcomp", (subcomp != NULL));

    dtstart = icalcomponent_get_dtstart(subcomp);
    dtend = icalcomponent_get_dtend(subcomp);
    due = icalcomponent_get_due(subcomp);

    ok("DTSTART is UTC", (icaltime_is_utc(dtstart)));
    ok("DTEND is my_zone", (strcmp(icaltimezone_get_tzid((icaltimezone *) dtend.zone), "my_zone") == 0));
    ok("DUE is UTC", (icaltime_is_utc(due)));

    for (prop = icalcomponent_get_first_property(subcomp, ICAL_EXDATE_PROPERTY), idx = 0;
         prop;
         prop = icalcomponent_get_next_property(subcomp, ICAL_EXDATE_PROPERTY), idx++) {
        struct icaltimetype exdate = icalproperty_get_exdate (prop);

        ok("EXDATE is not null-time", (!icaltime_is_null_time (exdate)));
        ok("expected EXDATE not NULL", (exdate_expected[idx].tzid != NULL));

        if (*(exdate_expected[idx].tzid)) {
            ok("EXDATE zone is not NULL", (exdate.zone != NULL));
            ok("EXDATE matches timezone", (strcmp(exdate_expected[idx].tzid, icaltimezone_get_tzid((icaltimezone *) exdate.zone)) == 0));
        } else {
            ok("EXDATE zone is NULL", (exdate.zone == NULL));
        }

        ok("EXDATE is excluded", (icalproperty_recurrence_is_excluded(subcomp, &dtstart, &exdate)));
    }

    icalcomponent_free (comp);
}

void test_kind_to_string(void)
{
    // value testing
    ok("VALUE_KIND ICAL_ANY_VALUE is NULL",
       (icalvalue_kind_to_string(ICAL_ANY_VALUE) == NULL));
    str_is("VALUE_KIND ICAL_POLLCOMPLETION_VALUE",
           icalvalue_kind_to_string(ICAL_POLLCOMPLETION_VALUE), "POLLCOMPLETION");
    str_is("VALUE_KIND ICAL_NO_VALUE",
           icalvalue_kind_to_string(ICAL_NO_VALUE), "");

    ok("VALUE_KIND ICAL_ANY_VALUE is invalid",
       !icalvalue_kind_is_valid(ICAL_ANY_VALUE));
    ok("VALUE_KIND ICAL_POLLCOMPLETION_VALUE is valid",
       icalvalue_kind_is_valid(ICAL_POLLCOMPLETION_VALUE));
    ok("VALUE_KIND ICAL_NO_VALUE is valid",
       icalvalue_kind_is_valid(ICAL_NO_VALUE));

    // parameter testing
    ok("PARAMETER_KIND ICAL_ANY_PARAMETER is NULL",
       (icalparameter_kind_to_string(ICAL_ANY_PARAMETER) == NULL));
    str_is("PARAMETER_KIND ICAL_EMAIL_PARAMETER",
           icalparameter_kind_to_string(ICAL_EMAIL_PARAMETER), "EMAIL");
    str_is("PARAMETER_KIND ICAL_NO_PARAMETER",
           icalparameter_kind_to_string(ICAL_NO_PARAMETER), "");

    ok("PARAMETER_KIND ICAL_ANY_PARAMETER is invalid",
       !icalparameter_kind_is_valid(ICAL_ANY_PARAMETER));
    ok("PARAMETER_KIND ICAL_EMAIL_PARAMETER is valid",
       icalparameter_kind_is_valid(ICAL_EMAIL_PARAMETER));
    ok("PARAMETER_KIND ICAL_NO_PARAMETER is valid",
       icalparameter_kind_is_valid(ICAL_NO_PARAMETER));

    // property testing
    ok("PROPERTY_KIND ICAL_ANY_PROPERTY is NULL",
       (icalproperty_kind_to_string(ICAL_ANY_PROPERTY) == NULL));
    str_is("PROPERTY_KIND ICAL_VOTER_PROPERTY",
           icalproperty_kind_to_string(ICAL_VOTER_PROPERTY), "VOTER");
    str_is("PROPERTY_KIND ICAL_NO_PROPERTY",
           icalproperty_kind_to_string(ICAL_NO_PROPERTY), "");

    ok("PROPERTY_KIND ICAL_ANY_PROPERTY is invalid",
       !icalproperty_kind_is_valid(ICAL_ANY_PROPERTY));
    ok("PROPERTY_KIND ICAL_VOTER_PROPERTY is valid",
       icalproperty_kind_is_valid(ICAL_VOTER_PROPERTY));
    ok("PROPERTY_KIND ICAL_NO_PROPERTY is valid",
       icalproperty_kind_is_valid(ICAL_NO_PROPERTY));
}

void test_string_to_kind(void)
{
    ok("VALUE NULL is ICAL_NO_VALUE",
       (icalvalue_string_to_kind(NULL) == ICAL_NO_VALUE));
    int_is("ICAL_POLLCOMPLETION_VALUE is POLLCOMPLETION",
           icalvalue_string_to_kind("POLLCOMPLETION"), ICAL_POLLCOMPLETION_VALUE);
    int_is("ICAL_NO_VALUE is empty string",
           icalvalue_string_to_kind(""), ICAL_NO_VALUE);

    ok("PARAMETER NULL is ICAL_NO_PARAMETER",
       (icalparameter_string_to_kind(NULL) == ICAL_NO_PARAMETER));
    int_is("ICAL_EMAIL_PARAMETER is EMAIL",
           icalparameter_string_to_kind("EMAIL"), ICAL_EMAIL_PARAMETER);
    int_is("ICAL_NO_PARAMETER is empty string",
           icalparameter_string_to_kind(""), ICAL_NO_PARAMETER);

    ok("PROPERTY NULL is ICAL_NO_PROPERTY",
       (icalproperty_string_to_kind(NULL) == ICAL_NO_PROPERTY));
    int_is("ICAL_VOTER_PROPERTY is VOTER",
           icalproperty_string_to_kind("VOTER"), ICAL_VOTER_PROPERTY);
    int_is("ICAL_NO_PROPERTY is empty string",
           icalproperty_string_to_kind(""), ICAL_NO_PROPERTY);
}

void test_set_date_datetime_value(void)
{
    icalproperty *prop;
    icalparameter *param;
    struct icaltimetype itt;
    const char *propstr;

    itt = icaltime_current_time_with_zone(icaltimezone_get_utc_timezone());

    prop = icalproperty_new_dtstart(itt);
    propstr = icalproperty_as_ical_string(prop);
    icalproperty_free(prop);
    prop = icalproperty_new_from_string(propstr);
    ok("DATE-TIME on DTSTART is saved without VALUE",
       (icalproperty_get_first_parameter(prop, ICAL_VALUE_PARAMETER) == NULL));

    itt.is_date = 1;
    icalproperty_set_dtstart(prop, itt);
    propstr = icalproperty_as_ical_string(prop);
    icalproperty_free(prop);
    prop = icalproperty_new_from_string(propstr);
    param = icalproperty_get_first_parameter(prop, ICAL_VALUE_PARAMETER);
    ok("DATE on DTSTART is saved with VALUE", (param != NULL));
    ok("DATE on DTSTART is saved with DATE VALUE", (icalparameter_get_value(param) == ICAL_VALUE_DATE));

    itt.is_date = 0;
    icalproperty_set_dtstart(prop, itt);
    ok("VALUE parameter on DTSTART is removed right after set",
       (icalproperty_get_first_parameter(prop, ICAL_VALUE_PARAMETER) == NULL));
    propstr = icalproperty_as_ical_string(prop);
    icalproperty_free(prop);
    prop = icalproperty_new_from_string(propstr);
    ok("DATE-TIME on DTSTART is saved without VALUE",
       (icalproperty_get_first_parameter(prop, ICAL_VALUE_PARAMETER) == NULL));

    /* Try it twice */
    itt.is_date = 1;
    icalproperty_set_dtstart(prop, itt);
    propstr = icalproperty_as_ical_string(prop);
    icalproperty_free(prop);
    prop = icalproperty_new_from_string(propstr);
    param = icalproperty_get_first_parameter(prop, ICAL_VALUE_PARAMETER);
    ok("DATE on DTSTART is saved with VALUE", (param != NULL));
    ok("DATE on DTSTART is saved with DATE VALUE", (icalparameter_get_value(param) == ICAL_VALUE_DATE));

    icalproperty_free(prop);
}

void test_timezone_from_builtin(void)
{
    const char *strcomp_fmt =
        "BEGIN:VCALENDAR\r\n"
        "BEGIN:VTIMEZONE\r\n"
        "TZID:my_zone\r\n"
        "BEGIN:STANDARD\r\n"
        "TZNAME:my_zone\r\n"
        "DTSTART:19160429T230000\r\n"
        "TZOFFSETFROM:+0100\r\n"
        "TZOFFSETTO:+0200\r\n"
        "RRULE:FREQ=YEARLY;UNTIL=19160430T220000Z;BYDAY=-1SU;BYMONTH=4\r\n"
        "END:STANDARD\r\n"
        "END:VTIMEZONE\r\n"
        "BEGIN:VEVENT\r\n"
        "UID:0\r\n"
        "DTSTART;TZID=my_zone:20180101T010000\r\n"
        "DTEND;TZID=%s:20180101T030000\r\n"
        "DUE;TZID=Europe/Berlin:20180101T030000\r\n"
        "END:VEVENT\r\n"
        "END:VCALENDAR\r\n";
    icalcomponent *comp, *subcomp;
    icaltimezone *zone;
    struct icaltimetype dtstart, dtend, due;
    char *strcomp, *tzidprefix, *prevslash = NULL, *prevprevslash = NULL, *p;
    size_t len;

    /* Builtins are not enabled, zone loading will fail. */
    if (!icaltimezone_get_builtin_tzdata())
        return;

    zone = icaltimezone_get_builtin_timezone("America/New_York");
    tzidprefix = strdup(icaltimezone_get_tzid (zone));
    p = tzidprefix;
    while (p = strchr(p + 1, '/'), p) {
        prevprevslash = prevslash;
        prevslash = p;
    }
    if (prevprevslash) {
        prevprevslash[1] = 0;
    }

    icaltimezone_set_tzid_prefix(tzidprefix);

    len = strlen(strcomp_fmt) + strlen(icaltimezone_get_tzid(zone)) + 2;
    strcomp = (char *) malloc(len + 1);
    snprintf(strcomp, len, strcomp_fmt, icaltimezone_get_tzid(zone));

    comp = icalcomponent_new_from_string(strcomp);
    ok("icalcomponent_new_from_string()", (comp != NULL));
    subcomp = icalcomponent_get_first_component(comp, ICAL_VEVENT_COMPONENT);
    ok("get subcomp", (subcomp != NULL));

    dtstart = icalcomponent_get_dtstart(subcomp);
    dtend = icalcomponent_get_dtend(subcomp);
    due = icalcomponent_get_due(subcomp);

    ok("DTSTART is my_zone", (strcmp(icaltimezone_get_tzid((icaltimezone *) dtstart.zone), "my_zone") == 0));
    ok("DTEND is America/New_York", (strcmp(icaltimezone_get_location((icaltimezone *) dtend.zone), "America/New_York") == 0));
    ok("DUE is Europe/Berlin", (strcmp(icaltimezone_get_location((icaltimezone *) due.zone), "Europe/Berlin") == 0));

    icalcomponent_free(comp);
    free(tzidprefix);
    free(strcomp);

    zone = icaltimezone_get_builtin_timezone("Pacific/Midway");
    ok("builtin location is Pacific/Midway", (strcmp(icaltimezone_get_location((icaltimezone *) zone), "Pacific/Midway") == 0));
    comp = icaltimezone_get_component(zone);
    strcomp = icalcomponent_as_ical_string_r(comp);
    comp = icalcomponent_new_from_string(strcomp);
    free(strcomp);

    ok("VTIMEZONE icalcomponent_new_from_string()", (comp != NULL));

    zone = icaltimezone_new();
    ok("set icaltimezone component", (icaltimezone_set_component(zone, comp)));
    ok("read from string builtin location is still Pacific/Midway", (strcmp(icaltimezone_get_location((icaltimezone *) zone), "Pacific/Midway") == 0));
    icaltimezone_free(zone, 1);

    icaltimezone_set_tzid_prefix(TESTS_TZID_PREFIX);
}

void test_icalvalue_decode_ical_string(void)
{
    char buff[12];
    const char *defvalue, *value;

    /* Without escape characters */
    defvalue = "xxxxx|VALUE";
    strcpy(buff, defvalue);
    value = buff + 6;

    ok("Fails to decode into too small buffer", (icalvalue_decode_ical_string(value, buff, 4) == 0));
    ok("Buffer not changed", (strcmp(buff, defvalue) == 0));
    ok("Fails to decode into small buffer (only without nul-terminator)", (icalvalue_decode_ical_string(value, buff, 5) == 0));
    ok("Buffer not changed", (strcmp(buff, defvalue) == 0));
    ok("Decodes into large-enough buffer", (icalvalue_decode_ical_string(value, buff, 6) != 0));
    ok("Properly decoded", (strcmp(buff, value) == 0));

    /* With escape character */
    defvalue = "xxxxx|a\\\\b!";
    strcpy(buff, defvalue);
    value = buff + 6;

    ok("Fails to decode into too small buffer", (icalvalue_decode_ical_string(value, buff, 3) == 0));
    ok("Buffer not changed", (strcmp(buff, defvalue) == 0));
    ok("Fails to decode into small buffer (only without nul-terminator)", (icalvalue_decode_ical_string(value, buff, 4) == 0));
    ok("Buffer not changed", (strcmp(buff, defvalue) == 0));
    ok("Decodes into large-enough buffer", (icalvalue_decode_ical_string(value, buff, 5) != 0));
    ok("Properly decoded", (strcmp(buff, "a\\b!") == 0));

    /* With ending escape character, which will be ignored */
    defvalue = "xxxxx|a\\\\\\";
    strcpy(buff, defvalue);
    value = buff + 6;

    ok("Fails to decode into too small buffer", (icalvalue_decode_ical_string(value, buff, 1) == 0));
    ok("Buffer not changed", (strcmp(buff, defvalue) == 0));
    ok("Fails to decode into small buffer (only without nul-terminator)", (icalvalue_decode_ical_string(value, buff, 2) == 0));
    ok("Buffer not changed", (strcmp(buff, defvalue) == 0));
    ok("Decodes into large-enough buffer", (icalvalue_decode_ical_string(value, buff, 3) != 0));
    ok("Properly decoded", (strcmp(buff, "a\\") == 0));
}

void test_icalcomponent_normalize(void)
{
    const char *calStr1 =
        "BEGIN:VCALENDAR\n"
        "PRODID:-//ACME//NONSGML ACME Calendar//EN\n"
        "VERSION:2.0\n"
        "CALSCALE:GREGORIAN\n"
        "BEGIN:VTIMEZONE\n"
        "TZID:America/New_York\n"
        "BEGIN:DAYLIGHT\n"
        "TZNAME:EDT\n"
        "TZOFFSETFROM:-0500\n"
        "TZOFFSETTO:-0400\n"
        "DTSTART:20070311T020000\n"
        "RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\n"
        "END:DAYLIGHT\n"
        "BEGIN:STANDARD\n"
        "TZNAME:EST\n"
        "TZOFFSETFROM:-0400\n"
        "TZOFFSETTO:-0500\n"
        "DTSTART:20071104T020000\n"
        "RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\n"
        "END:STANDARD\n"
        "END:VTIMEZONE\n"
        "BEGIN:VEVENT\n"
        "DTSTART;VALUE=DATE-TIME:20180220T020000Z\n"
        "DURATION:PT1H\n"
        "END:VEVENT\n"
        "END:VCALENDAR\n";
    const char *calStr2 =
        "BEGIN:VCALENDAR\n"
        "VERSION:2.0\n"
        "PRODID:-//ACME//NONSGML ACME Calendar//EN\n"
        "BEGIN:VEVENT\n"
        "TRANSP:OPAQUE\n"
        "DURATION:PT1H\n"
        "DTSTART;VALUE=DATE-TIME:20180220T020000Z\n"
        "END:VEVENT\n"
        "BEGIN:VTIMEZONE\n"
        "TZID:America/New_York\n"
        "BEGIN:STANDARD\n"
        "TZNAME:EST\n"
        "TZOFFSETFROM:-0400\n"
        "TZOFFSETTO:-0500\n"
        "DTSTART:20071104T020000\n"
        "RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\n"
        "END:STANDARD\n"
        "BEGIN:DAYLIGHT\n"
        "TZNAME:EDT\n"
        "TZOFFSETFROM:-0500\n"
        "TZOFFSETTO:-0400\n"
        "DTSTART:20070311T020000\n"
        "RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\n"
        "END:DAYLIGHT\n"
        "END:VTIMEZONE\n"
        "END:VCALENDAR\n";

    icalcomponent *ical1 = icalcomponent_new_from_string(calStr1);
    icalcomponent *ical2 = icalcomponent_new_from_string(calStr2);

    icalcomponent_normalize(ical1);
    icalcomponent_normalize(ical2);

    calStr1 = icalcomponent_as_ical_string(ical1);
    calStr2 = icalcomponent_as_ical_string(ical2);

    icalcomponent_free(ical1);
    icalcomponent_free(ical2);

    if (VERBOSE) {
        printf("iCal1:\n%s\n\n", calStr1);
        printf("iCal2:\n%s\n\n", calStr2);
    }

    str_is("Normalized components match", calStr1, calStr2);
}

static void test_builtin_compat_tzid (void)
{
    struct _cases {
        const char *name;
        const char *tzid;
        int should_match;
    } cases[] = {
        { "Matches current TZID", "/freeassociation.sourceforge.net/Europe/London", 1 },
        { "Matches Tzfile compat TZID", "/freeassociation.sourceforge.net/Tzfile/Europe/London", 1 },
        { "Matches citadel.org TZID", "/citadel.org/20190914_1/Europe/London", 1 },
        { "Does not match custom TZID", "/custom/test/tzid/Europe/London", 0 },
        { NULL, NULL, 0 }
    };
    int ii, jj;
    icaltimezone *tz;

    for (jj = 0; jj < 2; jj++) {
        if(jj == 1) {
            icaltimezone_set_tzid_prefix("");
            icaltimezone_free_builtin_timezones();
        }

        tz = icaltimezone_get_builtin_timezone("Europe/London");

        for (ii = 0; cases[ii].tzid; ii++) {
            icaltimezone *zone;

            if(cases[ii].should_match) {
                zone = icaltimezone_get_builtin_timezone_from_tzid(cases[ii].tzid);
                ok(cases[ii].name, (zone == tz));
                if(zone != tz && VERBOSE) {
                    printf("Returned builtin zone (%s) doesn't match expected zone for TZID '%s'\n",
                        zone ? icaltimezone_get_location(zone) : "NULL",
                        cases[ii].tzid);
                }
            } else {
                zone = icaltimezone_get_builtin_timezone_from_tzid(cases[ii].tzid);
                ok(cases[ii].name, (!zone));
                if(zone != NULL && VERBOSE) {
                    printf("Returned builtin zone (%s), but it should fail for TZID '%s'\n",
                        icaltimezone_get_location(zone),
                        cases[ii].tzid);
                }
            }
        }
    }

    icaltimezone_set_tzid_prefix(TESTS_TZID_PREFIX);
    icaltimezone_free_builtin_timezones();
}

static void test_vcc_vcard_parse(void)
{
    /* Two VCARD-s, because some arches can parse the first and some the second. */
    const char *vcard1 =
	"BEGIN:VCARD\r\n"
	"VERSION:3.0\r\n"
	"NOTE:\r\n"
	"FN:Xxxx\r\n"
	"N:;Xxxx;;;\r\n"
	"END:VCARD";
    const char *vcard2 =
        "BEGIN:VCARD\r\n"
        "VERSION:3.0\r\n"
        "X-XXX-ORIGINAL-VCARD:\r\n"
        "X-XXX-KIND:XX_XXXXXXXX\r\n"
        "NOTE:\r\n"
        "X-XXXXXXXXX-FILE-AS:Xxxxxx\r\n"
        "FN:Xxxxxx\r\n"
        "N:;Xxxxxx;;;\r\n"
        "X-XXX-XXXXXXXXX:\r\n"
        "END:VCARD";
    const char *vcalendar =
	"BEGIN:VCALENDAR\r\n"
	"BEGIN:VEVENT\r\n"
	"UID:123\r\n"
	"SUMMARY:Summary\r\n"
	"DTSTAMP:20210803T063522Z\r\n"
	"DTSTART;VALUE=DATE:20210902\r\n"
	"END:VEVENT\r\n"
	"END:VCALENDAR\r\n";
    const char *vcalendar_broken =
	"BEGIN:VCALENDAR\r\n"
	"BEGIN:VTIMEZONE\r\n"
	"TZID:tz/id\r\n"
	"BEGIN:STANDARD\r\n"
	"TZNAME:PMT\r\n"
	"TZOFFSETFROM:+005744\r\n"
	"TZOFFSETTO:+005744\r\n"
	"DTSTART:18500101T000000\r\n"
	"END:STANDARD\r\n"
	"BEGIN:STANDARD\r\n"
	"TZNAME:CET\r\n"
	"TZOFFSETFROM:+005744\r\n"
	"TZOFFSETTO:+0100\r\n"
	"DTSTART:18911001T000000\r\n"
	"END:STANDARD\r\n"
	"BEGIN:DAYLIGHT\r\n"
	"TZNAME:CEST\r\n"
	"TZOFFSETFROM:+0100\r\n"
	"TZOFFSETTO:+0200\r\n"
	"DTSTART:19160430T230000\r\n"
	"END:DAYLIGHT\r\n"
	"END:VTIMEZONE\r\n"
	"BEGIN:VEVENT\r\n"
	"UID:321\r\n"
	"SUMMARY:Summary\r\n"
	"DTSTAMP:20210803T063522Z\r\n"
	"DTSTART;VALUE=DATE:20210902\r\n"
	"END:VEVENT\r\n"
	"BEGIN:VEVENT\r\n"
	"UID:123\r\n"
	"DTSTAMP:20210803T063522Z\r\n"
	"DTSTART;VALUE=DATE:20210902\r\n"
	"BEGIN:VALARM\r\n"
	"ACTION:DISPLAY\r\n"
	"TRIGGER:-PT15M\r\n"
	"END:VALARM\r\n"
	"DESCRIPTION:aaa \r\n"
	"\r\n"
	" aaa\\naaa 1\\n \r\n"
	"SUMMARY:Summary\r\n"
	"END:VEVENT\r\n"
	"END:VCALENDAR\r\n";
    VObject *vcal;

    vcal = Parse_MIME(vcard1, (unsigned long)strlen(vcard1));
    if(vcal) {
        icalcomponent *icalcomp;

        icalcomp = icalvcal_convert(vcal);
        ok("vCard1 is not iCalendar", (icalcomp == NULL));
        if(icalcomp)
            icalcomponent_free(icalcomp);

        cleanVObject (vcal);
    } else {
        ok("vCard1 cannot be parsed", (vcal == NULL));
    }

    vcal = Parse_MIME(vcard2, (unsigned long)strlen(vcard2));
    if(vcal) {
        icalcomponent *icalcomp;

        icalcomp = icalvcal_convert(vcal);
        ok("vCard2 is not iCalendar", (icalcomp == NULL));
        if(icalcomp)
            icalcomponent_free(icalcomp);

        cleanVObject(vcal);
    } else {
        ok("vCard2 cannot be parsed", (vcal == NULL));
    }

    vcal = Parse_MIME(vcalendar, (unsigned long)strlen(vcalendar));
    ok("vCalendar can be parsed", (vcal != NULL));
    if(vcal) {
        icalcomponent *icalcomp;

        icalcomp = icalvcal_convert(vcal);
        ok("vCalendar can be converted", (icalcomp != NULL));
        if(icalcomp) {
            icalcomponent *child;

            ok("vCalendar is VCALENDAR", (icalcomponent_isa(icalcomp) == ICAL_VCALENDAR_COMPONENT));
            ok("vCalendar has one child", (icalcomponent_count_components(icalcomp, ICAL_ANY_COMPONENT) == 1));
            child = icalcomponent_get_inner(icalcomp);
            ok("vCalendar has inner comp", (child != NULL && child != icalcomp));
            ok("vCalendar child is VEVENT", (icalcomponent_isa(child) == ICAL_VEVENT_COMPONENT));
            ok("vCalendar child UID matches", (strcmp(icalcomponent_get_uid(child), "123") == 0));
            ok("vCalendar child SUMMARY matches", (strcmp(icalcomponent_get_summary(child), "Summary") == 0));
            icalcomponent_free(icalcomp);
	}

        cleanVObject(vcal);
    }

    vcal = Parse_MIME(vcalendar_broken, (unsigned long)strlen(vcalendar_broken));
    ok("vCalendar-broken cannot be parsed", (vcal == NULL));
}

static void test_implicit_dtend_duration(void)
{
    const struct icaltimetype start1 = icaltime_from_string("20220108");
    icalcomponent* c = icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
            icalcomponent_vanew(
                ICAL_VEVENT_COMPONENT,
                icalproperty_vanew_dtstart(start1, (void *)0),
                (void *)0),
            (void *)0);
    struct icaldurationtype d = icalcomponent_get_duration(c);
    struct icaltimetype end = icalcomponent_get_dtend(c),
        start = icaltime_from_string("20220108T101010Z");
    if (VERBOSE) {
        printf("%s\n", icaldurationtype_as_ical_string(d));
    }
    str_is("icaldurationtype_as_ical_string(d)", "P1D", icaldurationtype_as_ical_string(d));

    if (VERBOSE) {
        printf("%s\n", icaltime_as_ical_string(end));
    }
    str_is("icaltime_as_ical_string(end)", "20220109", icaltime_as_ical_string(end));

    icalcomponent_set_dtstart(c, start);
    d = icalcomponent_get_duration(c);
    end = icalcomponent_get_dtend(c);
    if (VERBOSE) {
        printf("%s\n", icaldurationtype_as_ical_string(d));
    }
    int_is("icaldurationtype_as_int(d)", 0, icaldurationtype_as_int(d));

    if (VERBOSE) {
        printf("%s\n", icaltime_as_ical_string(end));
    }
    int_is("icaltime_compare(start, end)", 0, icaltime_compare(start, end));
    icalcomponent_free(c);


    c = icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
                icalcomponent_vanew(
                    ICAL_VTODO_COMPONENT,
                    icalproperty_vanew_dtstart(start1, (void *)0),
                    (void *)0),
            (void *)0);
    icalcomponent_set_due(c, icaltime_from_string("20220109"));
    d = icalcomponent_get_duration(c);
    end = icalcomponent_get_dtend(c);
    if (VERBOSE) {
        printf("%s\n", icaldurationtype_as_ical_string(d));
    }
    str_is("P1D", "P1D", icaldurationtype_as_ical_string(d));

    if (VERBOSE) {
        printf("%i\n", icaltime_is_null_time(end));
    }
    int_is("icaltime_is_null_time(end)", 1, icaltime_is_null_time(end));
    icalcomponent_free(c);
}

static void
test_icalvalue_resets_timezone_on_set(void)
{
    const char *strcomp =
        "BEGIN:VCALENDAR\r\n"
        "BEGIN:VTIMEZONE\r\n"
        "TZID:my_zone\r\n"
        "BEGIN:STANDARD\r\n"
        "TZNAME:my_zone\r\n"
        "DTSTART:19160429T230000\r\n"
        "TZOFFSETFROM:+0100\r\n"
        "TZOFFSETTO:+0200\r\n"
        "RRULE:FREQ=YEARLY;UNTIL=19160430T220000Z;BYDAY=-1SU;BYMONTH=4\r\n"
        "END:STANDARD\r\n"
        "END:VTIMEZONE\r\n"
        "BEGIN:VEVENT\r\n"
        "UID:0\r\n"
        "DTSTART;TZID=my_zone:20180101T010000\r\n"
        "DTEND:20180202T020000Z\r\n"
        "DUE:20180302T030000\r\n"
        "END:VEVENT\r\n"
        "END:VCALENDAR\r\n";
    icalcomponent *comp, *clone, *inner;
    icaltimetype comp_dtstart, comp_dtend, comp_due;
    icaltimetype clone_dtstart, clone_dtend, clone_due;
    const char *orig_str, *clone_str;
    int estate;

    estate = icalerror_get_errors_are_fatal();
    icalerror_set_errors_are_fatal(0);

    /* First try without calling 'set' */
    comp = icalcomponent_new_from_string(strcomp);
    ok("1st - vCalendar can be parsed", (comp != NULL));
    inner = icalcomponent_get_inner(comp);
    ok("1st - inner exists", (inner != NULL));
    orig_str = icalcomponent_as_ical_string(inner);
    comp_dtstart = icalcomponent_get_dtstart(inner);
    comp_dtend = icalcomponent_get_dtend(inner);
    comp_due = icalcomponent_get_due(inner);
    ok("1st - comp dtstart is non-UTC zone", (comp_dtstart.zone != NULL && comp_dtstart.zone != icaltimezone_get_utc_timezone()));
    ok("1st - comp dtend is UTC zone", (comp_dtend.zone == icaltimezone_get_utc_timezone()));
    ok("1st - comp due is floating", (comp_due.zone == NULL));
    clone = icalcomponent_new_clone(inner);
    icalcomponent_free(comp);
    /* note the comp_dtstart.zone points to a freed memory now (it was freed with the 'comp') */
    clone_dtstart = icalcomponent_get_dtstart(clone);
    clone_dtend = icalcomponent_get_dtend(clone);
    clone_due = icalcomponent_get_due(clone);
    ok("1st - clone dtstart is null zone", (clone_dtstart.zone == NULL));
    ok("1st - clone dtend is UTC zone", (clone_dtend.zone == icaltimezone_get_utc_timezone()));
    ok("1st - clone due is floating", (clone_due.zone == NULL));
    clone_str = icalcomponent_as_ical_string(clone);
    ok("1st - clone and orig components match", (strcmp(orig_str, clone_str) == 0));
    icalcomponent_free(clone);

    /* Second try with calling 'set' */
    comp = icalcomponent_new_from_string(strcomp);
    inner = icalcomponent_get_inner(comp);
    orig_str = icalcomponent_as_ical_string(inner);
    comp_dtstart = icalcomponent_get_dtstart(inner);
    comp_dtend = icalcomponent_get_dtend(inner);
    comp_due = icalcomponent_get_due(inner);
    ok("2nd - comp dtstart is non-UTC zone", (comp_dtstart.zone != NULL && comp_dtstart.zone != icaltimezone_get_utc_timezone()));
    ok("2nd - comp dtend is UTC zone", (comp_dtend.zone == icaltimezone_get_utc_timezone()));
    ok("2nd - comp due is floating", (comp_due.zone == NULL));
    icalcomponent_set_dtstart(inner, comp_dtstart);
    icalcomponent_set_dtend(inner, comp_dtend);
    icalcomponent_set_due(inner, comp_due);
    comp_dtstart = icalcomponent_get_dtstart(inner);
    comp_dtend = icalcomponent_get_dtend(inner);
    comp_due = icalcomponent_get_due(inner);
    ok("2nd - comp dtstart is non-UTC zone", (comp_dtstart.zone != NULL && comp_dtstart.zone != icaltimezone_get_utc_timezone()));
    ok("2nd - comp dtend is UTC zone after set", (comp_dtend.zone == icaltimezone_get_utc_timezone()));
    ok("2nd - comp due is floating after set", (comp_due.zone == NULL));
    clone = icalcomponent_new_clone(inner);
    icalcomponent_free(comp);
    /* note the comp_dtstart.zone points to a freed memory now (it was freed with the 'comp') */
    clone_dtstart = icalcomponent_get_dtstart(clone);
    clone_dtend = icalcomponent_get_dtend(clone);
    clone_due = icalcomponent_get_due(clone);
    ok("2nd - clone dtstart is null zone", (clone_dtstart.zone == NULL));
    ok("2nd - clone dtend is UTC zone", (clone_dtend.zone == icaltimezone_get_utc_timezone()));
    ok("2nd - clone due is floating", (clone_due.zone == NULL));
    clone_str = icalcomponent_as_ical_string(clone);
    ok("2nd - clone and orig components match", (strcmp(orig_str, clone_str) == 0));
    icalcomponent_free(clone);

    icalerror_set_errors_are_fatal(estate);
    icalerror_clear_errno();
}

static void test_comma_in_xproperty(void)
{
    // X-property value without explicit value type
    icalproperty *xproperty = icalproperty_new_from_string("X-TEST-PROPERTY:test,test");
    icalcomponent *c;

    c = icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
                icalcomponent_vanew(
                    ICAL_VEVENT_COMPONENT,
                    xproperty,
                    0),
            0);

    str_is("icalproperty_as_ical_string()", "X-TEST-PROPERTY:test,test\r\n", icalproperty_as_ical_string(icalcomponent_get_first_property(icalcomponent_get_inner(c), ICAL_X_PROPERTY)));

    icalcomponent_free(c);

    // X-property value with TEXT value type
    xproperty = icalproperty_new_from_string("X-TEST-PROPERTY;VALUE=TEXT:test\\,test");

    c = icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
                icalcomponent_vanew(
                    ICAL_VEVENT_COMPONENT,
                    xproperty,
                    0),
            0);

    str_is("icalproperty_as_ical_string()", "X-TEST-PROPERTY;VALUE=TEXT:test\\,test\r\n", icalproperty_as_ical_string(icalcomponent_get_first_property(icalcomponent_get_inner(c), ICAL_X_PROPERTY)));

    icalcomponent_free(c);
}

static void test_remove_tzid_from_due(void)
{
    icalproperty *due = icalproperty_vanew_due(icaltime_from_string("20220120T120000"), (void *)0);
    icalcomponent *c;

    icalproperty_add_parameter(due, icalparameter_new_tzid("America/New_York"));

    c = icalcomponent_vanew(
            ICAL_VCALENDAR_COMPONENT,
                icalcomponent_vanew(
                    ICAL_VTODO_COMPONENT,
                    due,
                    0),
            0);

    icalcomponent_set_due(c, icaltime_from_string("20220120"));
    str_is("icalproperty_as_ical_string()", "DUE;VALUE=DATE:20220120\r\n", icalproperty_as_ical_string(icalcomponent_get_first_property(icalcomponent_get_inner(c), ICAL_DUE_PROPERTY)));

    icalcomponent_free(c);
}

void test_icaltime_as_timet(void)
{
    icaltimetype tt;
    time_t expectedTimeT;

    ok("icaltime_from_string translates 19011231T235959Z to -1", icaltime_as_timet(icaltime_from_string("19011231T235959Z")) == -1);

    ok("icaltime_from_string translates 19020101T000000Z to -2145916800", icaltime_as_timet(icaltime_from_string("19020101T000000Z")) == -2145916800);
    ok("icaltime_from_string translates 19290519T000000Z to -1281916800", icaltime_as_timet(icaltime_from_string("19290519T000000Z")) == -1281916800);
    ok("icaltime_from_string translates 19561004T000000Z to -417916800", icaltime_as_timet(icaltime_from_string("19561004T000000Z")) == -417916800);
    ok("icaltime_from_string translates 19700101T000000Z to 0", icaltime_as_timet(icaltime_from_string("19700101T000000Z")) == 0);
    ok("icaltime_from_string translates 19700301T235959Z to 5183999", icaltime_as_timet(icaltime_from_string("19700301T235959Z")) == 5183999);
    ok("icaltime_from_string translates 19970717T235959Z to 869183999", icaltime_as_timet(icaltime_from_string("19970717T235959Z")) == 869183999);
    ok("icaltime_from_string translates 20241202T235959Z to 1733183999", icaltime_as_timet(icaltime_from_string("20241202T235959Z")) == 1733183999);
    ok("icaltime_from_string translates 20371231T235959Z to 2145916799", icaltime_as_timet(icaltime_from_string("20371231T235959Z")) == 2145916799);
#if (SIZEOF_TIME_T > 4)
    ok("icaltime_from_string translates 20520419T235959Z to 2597183999", icaltime_as_timet(icaltime_from_string("20520419T235959Z")) == 2597183999);
    ok("icaltime_from_string translates 20790905T235959Z to 3461183999", icaltime_as_timet(icaltime_from_string("20790905T235959Z")) == 3461183999);
    ok("icaltime_from_string translates 21000101T235959Z to 4102531199", icaltime_as_timet(icaltime_from_string("21000101T235959Z")) == 4102531199);
    ok("icaltime_from_string translates 21000301T235959Z to 4107628799", icaltime_as_timet(icaltime_from_string("21000301T235959Z")) == 4107628799);
    ok("icaltime_from_string translates 22370122T235959Z to 8427628799", icaltime_as_timet(icaltime_from_string("22370122T235959Z")) == 8427628799);
    ok("icaltime_from_string translates 23731215T235959Z to 12747628799", icaltime_as_timet(icaltime_from_string("23731215T235959Z")) == 12747628799);
    ok("icaltime_from_string translates 25101107T235959Z to 17067628799", icaltime_as_timet(icaltime_from_string("25101107T235959Z")) == 17067628799);
    ok("icaltime_from_string translates 25821231T235959Z to 19344441599", icaltime_as_timet(icaltime_from_string("25821231T235959Z")) == 19344441599);
    ok("icaltime_from_string translates 99991231T235959Z to 253402300799", icaltime_as_timet(icaltime_from_string("99991231T235959Z")) == 253402300799);

    tt = icaltime_from_string("99991231T235959Z");
    icaltime_adjust(&tt, 0, 0, 0, 1);
    ok("icaltime_from_string translates 100000101T000000Z to -1", icaltime_as_timet(tt) == -1);
#else
    ok("icaltime_from_string translates 20380118T000000Z to -1", icaltime_as_timet(icaltime_from_string("20380118T000000Z")) == -1);
#endif

    tt = icaltime_from_string("19020101T000000Z");
    expectedTimeT = -2145916800;

#if (SIZEOF_TIME_T > 4)
    // Going through each day until 10000 takes ~250ms on a reasonably powered year 2020 business laptop.
    while (tt.year < 10000)
#else
    while ((tt.year < 2038) || ((tt.year == 2038) && (tt.month == 1) && (tt.day <= 17)))
#endif
    {
        time_t actualTimeT = icaltime_as_timet(tt);
        if (actualTimeT != expectedTimeT) {
            ok("icaltime_as_timet translates correctly", actualTimeT == expectedTimeT);
        }

        icaltime_adjust(&tt, 1, 0, 0, 0);
        expectedTimeT += (24 * 60 * 60);
    }

    ok("icaltime_as_timet translates out of bounds correctly", icaltime_as_timet(tt) == -1);
}

void test_icalcomponent_with_lastmodified(void)
{
    /* for https://github.com/libical/libical/issues/585 */

    icalcomponent *comp;
    struct icaltimetype lm = icaltime_from_timet_with_zone(1661280150, 0, NULL);
    comp = icalcomponent_vanew(ICAL_VCALENDAR_COMPONENT,
                               icalproperty_new_version("2.0"),
                               icalproperty_new_prodid("PROD-ABC"),
                               icalproperty_new_uid("1234abcd"),
                               icalproperty_new_lastmodified(lm),
                               icalproperty_new_name("name1"),
                               NULL); /* NOT 0! */
    icalcomponent_free(comp);
}

int main(int argc, char *argv[])
{
#if !defined(HAVE_UNISTD_H)
    extern char *optarg;
    extern int optopt;
#endif
#if defined(HAVE_GETOPT)
    int errflg = 0;
    int c;
#endif
    int do_test = 0;
    int do_header = 0;
    int failed_count = 0;

    set_zone_directory(TEST_ZONEDIR);
    icaltimezone_set_tzid_prefix(TESTS_TZID_PREFIX);

    test_start(0);

#if defined(HAVE_GETOPT)
    while ((c = getopt(argc, argv, "lvq")) != -1) {
        switch (c) {
        case 'v':
        {
            VERBOSE = 1;
            break;
        }
        case 'q':
        {
            QUIET = 1;
            break;
        }
        case 'l':
        {
            do_header = 1;
            break;
        }
        case '?':
        {
            errflg++;
        }
        }
    }

    if (errflg > 0) {
        fprintf(stderr, "Usage: %s [-v|-q|-l]\n", strrchr(argv[0],'/'));
        exit(1);
    }

    if (optind < argc) {
        do_test = atoi(argv[argc - 1]);
    }
#else
    if (argc > 1)
        do_test = atoi(argv[1]);

#endif

    test_run("Test time parser functions", test_time_parser, do_test, do_header);
    test_run("Test icaltime_as_timet", test_icaltime_as_timet, do_test, do_header);
    test_run("Test time", test_time, do_test, do_header);
    test_run("Test day of Year", test_doy, do_test, do_header);
    test_run("Test duration", test_duration, do_test, do_header);
    test_run("Test period", test_period, do_test, do_header);
    test_run("Test DTSTART", test_dtstart, do_test, do_header);
    test_run("Test day of year of week start", test_start_of_week, do_test, do_header);
    test_run("Test recur parser", test_recur_parser, do_test, do_header);
    test_run("Test recur", test_recur, do_test, do_header);
    test_run("Test Recurring Events File", test_recur_file, do_test, do_header);
    test_run("Test parameter bug", test_recur_parameter_bug, do_test, do_header);
    test_run("Test Array Expansion", test_expand_recurrence, do_test, do_header);
    test_run("Test Free/Busy lists", test_fblist, do_test, do_header);
    test_run("Test Overlaps", test_overlaps, do_test, do_header);

    test_run("Test Span", test_icalcomponent_get_span, do_test, do_header);
    test_run("Test Gauge SQL", test_gauge_sql, do_test, do_header);
    test_run("Test Gauge Compare", test_gauge_compare, do_test, do_header);
    test_run("Test File Set", test_fileset, do_test, do_header);
    test_run("Test File Set (Extended)", test_fileset_extended, do_test, do_header);
    test_run("Test Dir Set", test_dirset, do_test, do_header);
    test_run("Test Dir Set (Extended)", test_dirset_extended, do_test, do_header);

/* test_file_locks is slow but should work ok -- uncomment to test it */
/*    test_run("Test File Locks", test_file_locks, do_test, do_header);*/
    test_run("Test X Props and Params", test_x, do_test, do_header);
    test_run("Test Trigger", test_trigger, do_test, do_header);
    test_run("Test Restriction", test_restriction, do_test, do_header);
    test_run("Test RDATE", test_rdate, do_test, do_header);
    test_run("Test language binding", test_langbind, do_test, do_header);
    test_run("Test property parser", test_property_parse, do_test, do_header);
    test_run("Test Action", test_action, do_test, do_header);
    test_run("Test Value Parameter", test_value_parameter, do_test, do_header);
    test_run("Test Empty Parameter", test_empty_parameter, do_test, do_header);
    test_run("Test X property", test_x_property, do_test, do_header);
    test_run("Test X parameter", test_x_parameter, do_test, do_header);
    test_run("Test request status", test_requeststat, do_test, do_header);
    test_run("Test UTC-OFFSET", test_utcoffset, do_test, do_header);
    test_run("Test Values", test_values, do_test, do_header);
    test_run("Test Parameters", test_parameters, do_test, do_header);
    test_run("Test Properties", test_properties, do_test, do_header);
    test_run("Test Components", test_components, do_test, do_header);
    test_run("Test icalcomponent_foreach_recurrence", test_component_foreach, do_test, do_header);
    test_run("Test icalrecur_iterator_set_start with date", test_recur_iterator_set_start, do_test, do_header);
    test_run("Test weekly icalrecur_iterator on January 1", test_recur_iterator_on_jan_1, do_test, do_header);
    test_run("Test Convenience", test_convenience, do_test, do_header);
    test_run("Test classify ", test_classify, do_test, do_header);
    test_run("Test Iterators", test_iterators, do_test, do_header);
    test_run("Test strings", test_strings, do_test, do_header);
    test_run("Test TZID escaping", test_tzid_escape, do_test, do_header);
    test_run("Test Compare", test_compare, do_test, do_header);
    test_run("Create Simple Component", create_simple_component, do_test, do_header);
    test_run("Create Components", create_new_component, do_test, do_header);
    test_run("Create Components with vaargs", create_new_component_with_va_args, do_test,
             do_header);
    test_run("Test Memory", test_memory, do_test, do_header);
    test_run("Test Attachment", test_attach, do_test, do_header);
    test_run("Test CalDAV Attachment", test_attach_caldav, do_test, do_header);
    test_run("Test Attachment with URL", test_attach_url, do_test, do_header);
    test_run("Test Attachment with data", test_attach_data, do_test, do_header);
    test_run("Test icalcalendar", test_calendar, do_test, do_header);
    test_run("Test Dirset", test_dirset, do_test, do_header);
    test_run("Test vCal to iCal conversion", test_vcal, do_test, do_header);
    test_run("Test UTF-8 Handling", test_utf8, do_test, do_header);
    test_run("Test icaltime_compare UTC and zone handling", test_icaltime_compare_utc_zone, do_test, do_header);
    test_run("Test exclusion of recurrences as per r961", test_recurrenceexcluded, do_test,
             do_header);
#if ADD_TESTS_REQUIRING_INVESTIGATION
    test_run("Test bad dtstart in timezone as per r960", test_bad_dtstart_in_timezone, do_test,
             do_header);
#endif
    test_run("Test icalcomponent_new_from_string()", test_icalcomponent_new_from_string, do_test,
             do_header);
    test_run("Test comma in quoted value of x property", test_comma_in_quoted_value, do_test,
             do_header);
    test_run("Test TZID with UTC time", test_tzid_with_utc_time, do_test, do_header);
    test_run("Test kind_to_string", test_kind_to_string, do_test, do_header);
    test_run("Test string_to_kind", test_string_to_kind, do_test, do_header);
    test_run("Test set DATE/DATE-TIME VALUE", test_set_date_datetime_value, do_test, do_header);
    test_run("Test timezone from builtin", test_timezone_from_builtin, do_test, do_header);
    test_run("Test icalvalue_decode_ical_string", test_icalvalue_decode_ical_string, do_test, do_header);

    test_run("Test icalcomponent_normalize", test_icalcomponent_normalize, do_test, do_header);
    test_run("Test builtin compat TZID", test_builtin_compat_tzid, do_test, do_header);
    test_run("Test VCC vCard parse", test_vcc_vcard_parse, do_test, do_header);
    test_run("Test implicit DTEND and DURATION for VEVENT and VTODO", test_implicit_dtend_duration, do_test, do_header);
    test_run("Test icalvalue resets timezone on set", test_icalvalue_resets_timezone_on_set, do_test, do_header);
    test_run("Test removing TZID from DUE with icalcomponent_set_due", test_remove_tzid_from_due, do_test, do_header);
    test_run("Test commas in x-property", test_comma_in_xproperty, do_test, do_header);
    test_run("Test icalcomponent_vanew with lastmodified property", test_icalcomponent_with_lastmodified, do_test, do_header);

    /** OPTIONAL TESTS go here... **/

#if defined(WITH_CXX_BINDINGS)
    test_run("Test C++ API", test_cxx, do_test, do_header);
#endif

#if defined(HAVE_BDB)
    test_run("Test BDB Set", test_bdbset, do_test, do_header);
#endif

    icaltimezone_free_builtin_timezones();
    icalmemory_free_ring();
    free_zone_directory();

    failed_count = test_end();

    /* return a non-zero exit code if a test failed */
    return failed_count > 0;
}

/* make sure to keep tabs in our test strings */
/* Local Variables:    */
/* mode: c             */
/* indent-tabs-mode: t */
/* End:                */
