org.silverpeas.core.calendar.icalendar.ICal4JExporterTest.java Source code

Java tutorial

Introduction

Here is the source code for org.silverpeas.core.calendar.icalendar.ICal4JExporterTest.java

Source

/*
 * Copyright (C) 2000 - 2018 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception. You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "http://www.silverpeas.org/docs/core/legal/floss_exception.html"
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.silverpeas.core.calendar.icalendar;

import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.silverpeas.core.admin.user.model.User;
import org.silverpeas.core.admin.user.service.UserProvider;
import org.silverpeas.core.calendar.Attendee;
import org.silverpeas.core.calendar.Calendar;
import org.silverpeas.core.calendar.CalendarEvent;
import org.silverpeas.core.calendar.CalendarEventStubBuilder;
import org.silverpeas.core.calendar.CalendarMockBuilder;
import org.silverpeas.core.calendar.DayOfWeekOccurrence;
import org.silverpeas.core.calendar.Priority;
import org.silverpeas.core.calendar.Recurrence;
import org.silverpeas.core.calendar.ical4j.ICal4JDateCodec;
import org.silverpeas.core.calendar.ical4j.ICal4JExporter;
import org.silverpeas.core.calendar.ical4j.ICal4JRecurrenceCodec;
import org.silverpeas.core.date.Period;
import org.silverpeas.core.date.TimeUnit;
import org.silverpeas.core.importexport.ExportDescriptor;
import org.silverpeas.core.importexport.ExportException;
import org.silverpeas.core.persistence.datasource.OperationContext;
import org.silverpeas.core.persistence.datasource.model.jpa.JpaPersistOperation;
import org.silverpeas.core.persistence.datasource.model.jpa.JpaUpdateOperation;
import org.silverpeas.core.test.extention.EnableSilverTestEnv;
import org.silverpeas.core.test.extention.TestManagedBean;
import org.silverpeas.core.test.extention.TestManagedBeans;
import org.silverpeas.core.test.extention.TestedBean;
import org.silverpeas.core.util.StringUtil;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
import java.util.stream.Stream;

import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.silverpeas.core.calendar.Attendee.PresenceStatus.INFORMATIVE;
import static org.silverpeas.core.calendar.Attendee.PresenceStatus.OPTIONAL;
import static org.silverpeas.core.calendar.VisibilityLevel.PRIVATE;
import static org.silverpeas.core.calendar.icalendar.ICalendarExporter.CALENDAR;
import static org.silverpeas.core.calendar.icalendar.ICalendarExporter.HIDE_PRIVATE_DATA;
import static org.silverpeas.core.util.CollectionUtil.asList;

/**
 * @author Yohann Chastagnier
 */
@EnableSilverTestEnv
@TestManagedBeans({ JpaPersistOperation.class, JpaUpdateOperation.class })
public class ICal4JExporterTest {

    private static final Calendar calendar = CalendarMockBuilder.from("instanceId").withId("calendarUuid")
            .atZoneId(ZoneId.of("Europe/Paris")).build();

    @TestManagedBean
    private ICal4JDateCodec dateCodec = new ICal4JDateCodec();
    @TestManagedBean
    private ICal4JRecurrenceCodec recurrenceCodec = new ICal4JRecurrenceCodec(dateCodec);
    @TestedBean
    private ICal4JExporter iCalendarExporter;
    private User creator;

    @BeforeEach
    public void setup() {
        creator = mock(User.class);
        when(creator.getId()).thenReturn("creatorId");
        when(creator.getDisplayedName()).thenReturn("Creator Test");
        when(creator.geteMail()).thenReturn("creator.test@silverpeas.org");
        when(UserProvider.get().getUser(creator.getId())).thenReturn(creator);

        OperationContext.fromUser("0");
    }

    @Test
    public void undefinedListOfEvents() {
        assertThrows(ExportException.class, () -> iCalendarExporter.exports(
                ExportDescriptor.withOutputStream(new ByteArrayOutputStream()).withParameter(CALENDAR, calendar),
                () -> null));
    }

    @Test
    public void undefinedCalendar() {
        assertThrows(ExportException.class, () -> {
            iCalendarExporter.exports(ExportDescriptor.withOutputStream(new ByteArrayOutputStream()), () -> {
                List<CalendarEvent> calendarEvents = emptyList();
                return calendarEvents.stream();
            });
        });
    }

    @Test
    public void emptyListOfEvents() throws ExportException {
        exportAndVerifyResult(newExportDescriptor(), emptyList(), "ical4j_export_no_event.txt");
    }

    @Test
    public void simpleOneOfTwoDaysDuration() throws ExportException {
        // between December 14 and December 15 == [December 14, December 16[
        CalendarEvent event = withTwoDaysDuration().build();

        exportAndVerifyResult(newExportDescriptor(), singletonList(event),
                "ical4j_export_simple_two_days_duration.txt");
    }

    @Test
    public void simpleOneOfTwoDaysDurationButHiddenDataWhenPrivate() throws ExportException {
        // between December 14 and December 15 == [December 14, December 16[
        CalendarEvent event = withTwoDaysDuration().build();

        exportAndVerifyResult(newHiddenDataExportDescriptor(), singletonList(event),
                "ical4j_export_simple_two_days_duration.txt");
    }

    @Test
    public void simplePrivateOneOfTwoDaysDurationButHiddenDataWhenPrivate() throws ExportException {
        // between December 14 and December 15 == [December 14, December 16[
        CalendarEvent event = withTwoDaysDuration().withVisibilityLevel(PRIVATE).build();

        exportAndVerifyResult(newHiddenDataExportDescriptor(), singletonList(event),
                "ical4j_export_simple_two_days_duration_hidden_data.txt");
    }

    public CalendarEventStubBuilder withTwoDaysDuration() {
        return CalendarEventStubBuilder.from(Period.between(date("2016-12-14"), date("2016-12-16")))
                .plannedOn(calendar).withId("EVENT-UUID").withTitle("EVENT-TITLE")
                .withDescription("EVENT-DESCRIPTION <a href=\"#\">Click me...</a> !!!").withLocation("Grenoble")
                .withAttribute("url", "http://www.silverpeas.org/events/EVENT-UUID")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"));
    }

    @Test
    public void simpleOneWithTwoHoursDuration() throws ExportException {
        // between December 14, 12H30 and December 14, 14:30 ==  December 14 [12H30, 14H30[
        CalendarEvent event = CalendarEventStubBuilder
                .from(Period.between(datetime("2016-12-14T12:30:00Z"), datetime("2016-12-14T14:30:00Z")))
                .plannedOn(calendar).withId("EVENT-UUID").withTitle("EVENT-TITLE")
                .withDescription("EVENT-DESCRIPTION <a href=\"#\">Click me...</a> !!!").withLocation("Grenoble")
                .withAttribute("url", "http://www.silverpeas.org/events/EVENT-UUID")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z")).build();

        exportAndVerifyResult(newExportDescriptor(), singletonList(event),
                "ical4j_export_simple_two_hours_duration.txt");
    }

    @Test
    public void categorizedOneOfOneDayDuration() throws ExportException {
        CalendarEvent event = CalendarEventStubBuilder.from(Period.between(date("2016-12-25"), date("2016-12-25")))
                .plannedOn(calendar).withId("EVENT-UUID").withTitle("EVENT-TITLE-CATEGORIZED")
                .withCategories("Work", "Project").withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z")).build();

        exportAndVerifyResult(newExportDescriptor(), singletonList(event),
                "ical4j_export_categorized_one_day_duration.txt");
    }

    @Test
    public void oneOfTwoHoursDurationAndDailyRecurrence() throws ExportException {
        CalendarEvent event = CalendarEventStubBuilder
                .from(Period.between(datetime("2016-12-14T12:30:00Z"), datetime("2016-12-14T14:30:00Z")))
                .plannedOn(calendar).withId("EVENT-UUID").withTitle("EVENT-TITLE")
                .withDescription("EVENT-DESCRIPTION <a href=\"#\">Click me...</a> !!!").withLocation("Grenoble")
                .withAttribute("url", "http://www.silverpeas.org/events/EVENT-UUID")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z")).withRecurrence(Recurrence.every(TimeUnit.DAY))
                .build();

        exportAndVerifyResult(newExportDescriptor(), singletonList(event),
                "ical4j_export_recurrent_two_hours_duration.txt");
    }

    @Test
    public void oneOfTwoHoursDurationAndDailyRecurrenceUntil10Days() throws ExportException {
        CalendarEvent event = CalendarEventStubBuilder
                .from(Period.between(datetime("2016-12-14T12:30:00Z"), datetime("2016-12-14T14:30:00Z")))
                .plannedOn(calendar).withId("EVENT-UUID").withTitle("EVENT-TITLE")
                .withDescription("EVENT-DESCRIPTION <a href=\"#\">Click me...</a> !!!").withLocation("Grenoble")
                .withAttribute("url", "http://www.silverpeas.org/events/EVENT-UUID")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(TimeUnit.DAY).until(datetime("2016-12-24T09:30:00Z"))).build();

        exportAndVerifyResult(newExportDescriptor(), singletonList(event),
                "ical4j_export_recurrent_two_hours_duration_until_10_days.txt");
    }

    @Test
    public void oneOfTwoHoursDurationAndDailyRecurrenceWithExceptions() throws ExportException {
        CalendarEvent event = CalendarEventStubBuilder
                .from(Period.between(datetime("2016-12-14T12:30:00Z"), datetime("2016-12-14T14:30:00Z")))
                .plannedOn(calendar).withId("EVENT-UUID").withTitle("EVENT-TITLE")
                .withDescription("EVENT-DESCRIPTION <a href=\"#\">Click me...</a> !!!").withLocation("Grenoble")
                .withAttribute("url", "http://www.silverpeas.org/events/EVENT-UUID")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z")).withRecurrence(Recurrence.every(TimeUnit.DAY)
                        .excludeEventOccurrencesStartingAt(date("2016-12-21"), datetime("2016-12-27T11:00:00Z")))
                .build();

        exportAndVerifyResult(newExportDescriptor(), singletonList(event),
                "ical4j_export_recurrent_with_exceptions_two_hours_duration.txt");
    }

    @Test
    public void oneOfOneDayDurationAndDailyRecurrenceUntil10Days() throws ExportException {
        CalendarEvent event = CalendarEventStubBuilder.from(Period.between(date("2016-12-14"), date("2016-12-15")))
                .plannedOn(calendar).withId("EVENT-UUID").withTitle("EVENT-TITLE")
                .withDescription("EVENT-DESCRIPTION <a href=\"#\">Click me...</a> !!!").withLocation("Grenoble")
                .withAttribute("url", "http://www.silverpeas.org/events/EVENT-UUID")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(TimeUnit.DAY).until(datetime("2016-12-24T09:00:00Z"))).build();

        exportAndVerifyResult(newExportDescriptor(), singletonList(event),
                "ical4j_export_recurrent_one_day_duration_until_10_days.txt");
    }

    @Test
    public void severalOfOneHourDurationAndDailyRecurrence() throws ExportException {
        CalendarEvent event1 = CalendarEventStubBuilder
                .from(Period.between(datetime("2016-12-15T12:32:00Z"), datetime("2016-12-15T13:32:00Z")))
                .plannedOn(calendar).withId("EVENT-UUID-1").withExternalId("EXT-EVENT-UUID-1")
                .withTitle("EVENT-TITLE-1").withPriority(Priority.HIGH).withVisibilityLevel(PRIVATE)
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z")).withRecurrence(Recurrence.every(TimeUnit.DAY))
                .build();
        CalendarEvent event2 = CalendarEventStubBuilder
                .from(Period.between(datetime("2016-12-15T13:50:00Z"), datetime("2016-12-15T14:50:00Z")))
                .plannedOn(calendar).withId("EVENT-UUID-2").withTitle("EVENT-TITLE-2")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(3, TimeUnit.DAY)).build();
        CalendarEvent event3 = CalendarEventStubBuilder
                .from(Period.between(datetime("2016-12-15T15:27:00Z"), datetime("2016-12-15T16:27:00Z")))
                .plannedOn(calendar).withId("EVENT-UUID-3").withTitle("EVENT-TITLE-3")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(4, TimeUnit.DAY).until(datetime("2016-12-31T15:27:00Z"))).build();
        CalendarEvent event4 = CalendarEventStubBuilder
                .from(Period.between(datetime("2016-12-15T18:45:00Z"), datetime("2016-12-15T20:15:00Z")))
                .plannedOn(calendar).withId("EVENT-UUID-4").withTitle("EVENT-TITLE-4")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(2, TimeUnit.DAY).until(10)).build();
        CalendarEvent event5 = CalendarEventStubBuilder
                .from(Period.between(datetime("2016-12-15T20:00:00Z"), datetime("2016-12-15T20:15:00Z")))
                .plannedOn(calendar).withId("EVENT-UUID-5").withTitle("EVENT-TITLE-5")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(TimeUnit.DAY).excludeEventOccurrencesStartingAt(date("2016-12-18"),
                        date("2016-12-20"), date("2016-12-25")))
                .build();
        CalendarEvent event6 = CalendarEventStubBuilder
                .from(Period.between(datetime("2016-12-15T20:30:00Z"), datetime("2016-12-15T20:45:00Z")))
                .plannedOn(calendar).withId("EVENT-UUID-6").withTitle("EVENT-TITLE-6")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z")).withRecurrence(Recurrence
                        .every(2, TimeUnit.WEEK).on(DayOfWeek.MONDAY, DayOfWeek.WEDNESDAY, DayOfWeek.FRIDAY))
                .build();
        CalendarEvent event7 = CalendarEventStubBuilder
                .from(Period.between(datetime("2016-12-15T21:00:00Z"), datetime("2016-12-15T21:15:00Z")))
                .plannedOn(calendar).withId("EVENT-UUID-7").withTitle("EVENT-TITLE-7")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(
                        Recurrence.every(1, TimeUnit.MONTH).on(DayOfWeekOccurrence.nth(3, DayOfWeek.FRIDAY)))
                .build();
        CalendarEvent event8 = CalendarEventStubBuilder
                .from(Period.between(datetime("2016-12-15T21:15:00Z"), datetime("2016-12-15T21:30:00Z")))
                .plannedOn(calendar).withId("EVENT-UUID-8").withTitle("EVENT-TITLE-8")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(TimeUnit.MONTH)).build();
        CalendarEvent event9 = CalendarEventStubBuilder
                .from(Period.between(datetime("2016-12-15T21:45:00Z"), datetime("2016-12-15T22:00:00Z")))
                .plannedOn(calendar).withId("EVENT-UUID-9").withTitle("EVENT-TITLE-9")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(TimeUnit.YEAR)).build();

        exportAndVerifyResult(newExportDescriptor(),
                asList(event1, event2, event3, event4, event5, event6, event7, event8, event9),
                "ical4j_export_several_with_recurrence.txt");
    }

    @Test
    public void severalOnAllDaysAndDailyRecurrence() throws ExportException {
        CalendarEvent event1 = CalendarEventStubBuilder.from(Period.between(date("2016-12-15"), date("2016-12-16")))
                .plannedOn(calendar).withId("EVENT-UUID-1").withExternalId("EXT-EVENT-UUID-1")
                .withTitle("EVENT-TITLE-1").withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z")).withRecurrence(Recurrence.every(TimeUnit.DAY))
                .build();
        CalendarEvent event2 = CalendarEventStubBuilder.from(Period.between(date("2016-12-15"), date("2016-12-16")))
                .plannedOn(calendar).withId("EVENT-UUID-2").withTitle("EVENT-TITLE-2")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(3, TimeUnit.DAY)).build();
        CalendarEvent event3 = CalendarEventStubBuilder.from(Period.between(date("2016-12-15"), date("2016-12-16")))
                .plannedOn(calendar).withId("EVENT-UUID-3").withTitle("EVENT-TITLE-3")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(4, TimeUnit.DAY).until(date("2016-12-31"))).build();
        CalendarEvent event4 = CalendarEventStubBuilder.from(Period.between(date("2016-12-15"), date("2016-12-16")))
                .plannedOn(calendar).withId("EVENT-UUID-4").withTitle("EVENT-TITLE-4")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(2, TimeUnit.DAY).until(10)).build();
        CalendarEvent event5 = CalendarEventStubBuilder.from(Period.between(date("2016-12-15"), date("2016-12-16")))
                .plannedOn(calendar).withId("EVENT-UUID-5").withTitle("EVENT-TITLE-5")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(TimeUnit.DAY).excludeEventOccurrencesStartingAt(date("2016-12-18"),
                        date("2016-12-20"), date("2016-12-25")))
                .build();
        CalendarEvent event6 = CalendarEventStubBuilder.from(Period.between(date("2016-12-15"), date("2016-12-16")))
                .plannedOn(calendar).withId("EVENT-UUID-6").withTitle("EVENT-TITLE-6")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z")).withRecurrence(Recurrence
                        .every(2, TimeUnit.WEEK).on(DayOfWeek.MONDAY, DayOfWeek.WEDNESDAY, DayOfWeek.FRIDAY))
                .build();
        CalendarEvent event7 = CalendarEventStubBuilder.from(Period.between(date("2016-12-15"), date("2016-12-16")))
                .plannedOn(calendar).withId("EVENT-UUID-7").withTitle("EVENT-TITLE-7")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(
                        Recurrence.every(1, TimeUnit.MONTH).on(DayOfWeekOccurrence.nth(3, DayOfWeek.FRIDAY)))
                .build();
        CalendarEvent event8 = CalendarEventStubBuilder.from(Period.between(date("2016-12-15"), date("2016-12-16")))
                .plannedOn(calendar).withId("EVENT-UUID-8").withTitle("EVENT-TITLE-8")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(TimeUnit.MONTH)).build();
        CalendarEvent event9 = CalendarEventStubBuilder.from(Period.between(date("2016-12-15"), date("2016-12-16")))
                .plannedOn(calendar).withId("EVENT-UUID-9").withTitle("EVENT-TITLE-9")
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z"))
                .withRecurrence(Recurrence.every(TimeUnit.YEAR)).build();

        exportAndVerifyResult(newExportDescriptor(),
                asList(event1, event2, event3, event4, event5, event6, event7, event8, event9),
                "ical4j_export_several_with_recurrence_on_all_day.txt");
    }

    @Test
    public void simpleOneOfTwoDayDurationWithAttendees() throws ExportException {
        User user = mock(User.class);
        when(user.getId()).thenReturn("userId");
        when(user.getDisplayedName()).thenReturn("User Test");
        when(user.geteMail()).thenReturn("user.test@silverpeas.org");
        when(UserProvider.get().getUser(user.getId())).thenReturn(user);
        CalendarEvent event = CalendarEventStubBuilder.from(Period.between(date("2016-12-14"), date("2016-12-16")))
                .plannedOn(calendar).withId("EVENT-UUID-ATTENDEES").withTitle("EVENT-TITLE").withCreator(creator)
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z")).withAttendee(user, a -> {
                    a.setPresenceStatus(OPTIONAL);
                    a.accept();
                }).withAttendee("external.1@silverpeas.org", a -> a.setPresenceStatus(INFORMATIVE))
                .withAttendee("external.2@silverpeas.org", Attendee::decline).build();

        exportAndVerifyResult(newExportDescriptor(), singletonList(event),
                "ical4j_export_simple_two_days_duration_with_attendees.txt");
    }

    @Test
    public void oneWithRecurrenceAndWithAttendees() throws ExportException {
        User user = mock(User.class);
        when(user.getId()).thenReturn("userId");
        when(user.getDisplayedName()).thenReturn("User Test");
        when(user.geteMail()).thenReturn("user.test@silverpeas.org");
        when(UserProvider.get().getUser(user.getId())).thenReturn(user);
        CalendarEvent event = CalendarEventStubBuilder.from(Period.between(date("2016-11-30"), date("2016-11-30")))
                .plannedOn(calendar).withId("EVENT-UUID-RECURRENCE-ATTENDEES").withTitle("EVENT-TITLE")
                .withRecurrence(Recurrence.every(TimeUnit.DAY).until(10)).withCreator(creator)
                .withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z")).withAttendee(user, a -> {
                    a.setPresenceStatus(OPTIONAL);
                    a.accept();
                }).withAttendee("external.1@silverpeas.org", a -> a.setPresenceStatus(INFORMATIVE))
                .withAttendee("external.2@silverpeas.org", Attendee::decline).build();

        exportAndVerifyResult(newExportDescriptor(), singletonList(event),
                "ical4j_export_one_with_recurrence_and_with_attendees.txt");
    }

    @Test
    public void oneWithRecurrenceAndWithAttendeesWhichAnsweredOnDate() throws ExportException {
        CalendarEvent event = withRecurrenceAndWithAttendeesWhichAnsweredOnDate().build();

        exportAndVerifyResult(newExportDescriptor(), singletonList(event),
                "ical4j_export_one_with_recurrence_and_with_attendees_which_answered_on_date.txt");
    }

    @Test
    public void oneWithRecurrenceAndWithAttendeesWhichAnsweredOnDateButHiddenDataWhenPrivate()
            throws ExportException {
        CalendarEvent event = withRecurrenceAndWithAttendeesWhichAnsweredOnDate().build();

        exportAndVerifyResult(newHiddenDataExportDescriptor(), singletonList(event),
                "ical4j_export_one_with_recurrence_and_with_attendees_which_answered_on_date.txt");
    }

    @Test
    public void onePrivateWithRecurrenceAndWithAttendeesWhichAnsweredOnDateButHiddenDataWhenPrivate()
            throws ExportException {
        CalendarEvent event = withRecurrenceAndWithAttendeesWhichAnsweredOnDate().withVisibilityLevel(PRIVATE)
                .build();

        exportAndVerifyResult(newHiddenDataExportDescriptor(), singletonList(event),
                "ical4j_export_one_with_recurrence_and_with_attendees_which_answered_on_date_hidden_data.txt");
    }

    public CalendarEventStubBuilder withRecurrenceAndWithAttendeesWhichAnsweredOnDate() {
        User user = mock(User.class);
        when(user.getId()).thenReturn("userId");
        when(user.getDisplayedName()).thenReturn("User Test");
        when(user.geteMail()).thenReturn("user.test@silverpeas.org");
        when(UserProvider.get().getUser(user.getId())).thenReturn(user);
        final String externalAttendeeId_1 = "external.1@silverpeas.org";
        final String externalAttendeeId_2 = "external.2@silverpeas.org";
        return CalendarEventStubBuilder.from(Period.between(date("2016-11-30"), date("2016-11-30")))
                .plannedOn(calendar).withId("EVENT-UUID-RECURRENCE-ATTENDEES-ON-DATE-ANSWER")
                .withTitle("EVENT-TITLE").withRecurrence(Recurrence.every(TimeUnit.DAY).until(10))
                .withCreator(creator).withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z")).withAttendee(user, a -> {
                    a.setPresenceStatus(OPTIONAL);
                    a.accept();
                }).withAttendee(externalAttendeeId_1, a -> a.setPresenceStatus(INFORMATIVE))
                .withAttendee(externalAttendeeId_2, Attendee::decline)
                .withOccurrenceOn(Period.between(date("2016-12-06"), date("2016-12-06")), o -> {
                    o.getAttendees().get(user.getId()).ifPresent(Attendee::decline);
                    o.getAttendees().get(externalAttendeeId_1).ifPresent(Attendee::decline);
                })
                .withOccurrenceOn(Period.between(date("2016-12-09"), date("2016-12-10")),
                        o -> o.getAttendees().get(user.getId()).ifPresent(Attendee::tentativelyAccept))
                .withOccurrenceOn(Period.between(date("2016-12-10"), date("2016-12-11")),
                        o -> o.getAttendees().get(externalAttendeeId_2).ifPresent(Attendee::accept));
    }

    @Test
    public void oneWithRecurrenceAndWithAttendeesDifferences() throws ExportException {
        User user = mock(User.class);
        when(user.getId()).thenReturn("userId");
        when(user.getDisplayedName()).thenReturn("User Test");
        when(user.geteMail()).thenReturn("user.test@silverpeas.org");
        when(UserProvider.get().getUser(user.getId())).thenReturn(user);
        final String externalAttendeeId_1 = "external.1@silverpeas.org";
        final String externalAttendeeId_2 = "external.2@silverpeas.org";
        CalendarEvent event = CalendarEventStubBuilder.from(Period.between(date("2016-11-30"), date("2016-11-30")))
                .plannedOn(calendar).withId("EVENT-UUID-RECURRENCE-ATTENDEES-ON-DATE-ANSWER")
                .withTitle("EVENT-TITLE").withRecurrence(Recurrence.every(TimeUnit.DAY).until(10))
                .withCreator(creator).withCreationDate(datetime("2016-12-01T14:30:00Z"))
                .withLastUpdateDate(datetime("2016-12-02T09:00:00Z")).withAttendee(user, a -> {
                    a.setPresenceStatus(OPTIONAL);
                    a.accept();
                }).withAttendee(externalAttendeeId_1, a -> a.setPresenceStatus(INFORMATIVE))
                .withAttendee(externalAttendeeId_2, Attendee::decline).withOccurrenceOn(
                        Period.between(datetime("2016-12-06T17:30:00Z"), datetime("2016-12-06T18:30:00Z")), o -> {
                            o.getAttendees().removeIf(a -> user.getId().equals(a.getId()));
                            o.getAttendees().get(externalAttendeeId_1).ifPresent(Attendee::decline);
                        })
                .withOccurrenceOn(Period.between(date("2016-12-09"), date("2016-12-10")), o -> {
                    o.getAttendees().get(user.getId()).ifPresent(Attendee::tentativelyAccept);
                    o.getAttendees().removeIf(a -> externalAttendeeId_2.equals(a.getId()));
                }).build();

        exportAndVerifyResult(newExportDescriptor(), singletonList(event),
                "ical4j_export_one_with_recurrence_and_with_attendees_differences.txt");
    }

    /**
     * Centralization of verification.<br>
     * <p>
     * The mechanism is the following:<br>
     * <p/>
     * the first parameter represent the list of calendar events to export and the second one is the
     * name of the file that contains the expected result.<br>
     * Each lines starting with '#' character is ignored.<br>
     * If the file content is equal to the result of export, the test is successfully verified.<br>
     * If not, the different lines between the file content and the export result are logged to the
     * console.<br>
     * Only event parts are verified from the contents.
     * </p>
     * @param descriptor
     * @param fileNameOfExpectedResult the name of the file that contains the expected export result.
     */
    @SuppressWarnings({ "unchecked", "Duplicates" })
    private void exportAndVerifyResult(final ExportDescriptor descriptor, List<CalendarEvent> calendarEvents,
            String fileNameOfExpectedResult) throws ExportException {
        try {

            ByteArrayOutputStream emptyExportResult = new ByteArrayOutputStream();
            iCalendarExporter.exports(
                    ExportDescriptor.withOutputStream(emptyExportResult).withParameter(CALENDAR, calendar),
                    Stream::<CalendarEvent>empty);

            List<String> empty = IOUtils.readLines(new StringReader(emptyExportResult.toString()));
            empty.remove(empty.size() - 1);

            iCalendarExporter.exports(descriptor, calendarEvents::stream);

            StringReader current = new StringReader(descriptor.getOutputStream().toString());
            StringReader expected = new StringReader(getFileContent(fileNameOfExpectedResult));

            final List<String> currentContentLines = IOUtils.readLines(current);
            currentContentLines.remove(currentContentLines.size() - 1);
            Iterator<String> it = currentContentLines.iterator();
            while (it.hasNext() && !empty.isEmpty()) {
                String currentLine = it.next();
                String expectedLine = empty.remove(0);
                assertThat(expectedLine, is(currentLine));
                it.remove();
            }

            // Line to ignore from expected result extracted from a file.
            final List<String> expectedContentLines = IOUtils.readLines(expected);
            expectedContentLines.removeIf(currentExpectedLine -> currentExpectedLine.startsWith("#"));

            String currentContent = StringUtil.join(currentContentLines, "\n");
            String expectedContent = StringUtil.join(expectedContentLines, "\n");

            // Removing DTSTAMP
            currentContent = currentContent.replaceAll("DTSTAMP.+\n",
                    "DTSTAMP:VALUE IS NOT VERIFIED BUT IS MANDATORY\n");

            assertThat(currentContent, is(expectedContent));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private ExportDescriptor newExportDescriptor() {
        return ExportDescriptor.withOutputStream(new ByteArrayOutputStream()).withParameter(CALENDAR, calendar);
    }

    public ExportDescriptor newHiddenDataExportDescriptor() {
        return newExportDescriptor().withParameter(HIDE_PRIVATE_DATA, true);
    }

    private String getFileContent(String fileName) {
        try (InputStream fileStream = getClass().getResourceAsStream(fileName)) {
            return StringUtil.join(IOUtils.readLines(fileStream), '\n');
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static LocalDate date(String date) {
        return LocalDate.parse(date);
    }

    private static OffsetDateTime datetime(String date) {
        return OffsetDateTime.parse(date);
    }

    static {
        // This static block permits to ensure that the UNIT TEST is entirely executed into UTC
        // TimeZone.
        TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC));
    }
}