@ -8,6 +8,7 @@ declare(strict_types=1);
namespace OCA\DAV\Tests\unit\CalDAV\WebcalCaching;
namespace OCA\DAV\Tests\unit\CalDAV\WebcalCaching;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Import\ImportService;
use OCA\DAV\CalDAV\WebcalCaching\Connection;
use OCA\DAV\CalDAV\WebcalCaching\Connection;
use OCA\DAV\CalDAV\WebcalCaching\RefreshWebcalService;
use OCA\DAV\CalDAV\WebcalCaching\RefreshWebcalService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\AppFramework\Utility\ITimeFactory;
@ -23,7 +24,8 @@ class RefreshWebcalServiceTest extends TestCase {
private CalDavBackend& MockObject $caldavBackend;
private CalDavBackend& MockObject $caldavBackend;
private Connection& MockObject $connection;
private Connection& MockObject $connection;
private LoggerInterface& MockObject $logger;
private LoggerInterface& MockObject $logger;
private ITimeFactory& MockObject $time;
private ImportService& MockObject $importService;
private ITimeFactory& MockObject $timeFactory;
protected function setUp(): void {
protected function setUp(): void {
parent::setUp();
parent::setUp();
@ -31,19 +33,32 @@ class RefreshWebcalServiceTest extends TestCase {
$this->caldavBackend = $this->createMock(CalDavBackend::class);
$this->caldavBackend = $this->createMock(CalDavBackend::class);
$this->connection = $this->createMock(Connection::class);
$this->connection = $this->createMock(Connection::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->time = $this->createMock(ITimeFactory::class);
$this->importService = $this->createMock(ImportService::class);
$this->timeFactory = $this->createMock(ITimeFactory::class);
// Default time factory behavior: current time is far in the future so refresh always happens
$this->timeFactory->method('getTime')->willReturn(PHP_INT_MAX);
$this->timeFactory->method('getDateTime')->willReturn(new \DateTime());
}
}
#[\PHPUnit\Framework\Attributes\DataProvider('runDataProvider')]
/**
public function testRun(string $body, string $contentType, string $result): void {
* Helper to create a resource stream from string content
$refreshWebcalService = $this->getMockBuilder(RefreshWebcalService::class)
*/
->onlyMethods(['getRandomCalendarObjectUri'])
private function createStreamFromString(string $content) {
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->time])
$stream = fopen('php://temp', 'r+');
->getMock();
fwrite($stream, $content);
rewind($stream);
return $stream;
}
$refreshWebcalService
#[\PHPUnit\Framework\Attributes\DataProvider('runDataProvider')]
->method('getRandomCalendarObjectUri')
public function testRun(string $body, string $format, string $result): void {
->willReturn('uri-1.ics');
$refreshWebcalService = new RefreshWebcalService(
$this->caldavBackend,
$this->logger,
$this->connection,
$this->timeFactory,
$this->importService
);
$this->caldavBackend->expects(self::once())
$this->caldavBackend->expects(self::once())
->method('getSubscriptionsForUser')
->method('getSubscriptionsForUser')
@ -71,26 +86,48 @@ class RefreshWebcalServiceTest extends TestCase {
],
],
]);
]);
$stream = $this->createStreamFromString($body);
$this->connection->expects(self::once())
$this->connection->expects(self::once())
->method('queryWebcalFeed')
->method('queryWebcalFeed')
->willReturn($result);
->willReturn(['data' => $stream, 'format' => $format]);
$this->caldavBackend->expects(self::once())
->method('getLimitedCalendarObjects')
->willReturn([]);
// Create a VCalendar object that will be yielded by the import service
$vCalendar = VObject\Reader::read($result);
$generator = function () use ($vCalendar) {
yield $vCalendar;
};
$this->importService->expects(self::once())
->method('importText')
->willReturn($generator());
$this->caldavBackend->expects(self::once())
$this->caldavBackend->expects(self::once())
->method('createCalendarObject')
->method('createCalendarObject')
->with(42, 'uri-1.ics', $result, 1);
->with(
'42',
self::matchesRegularExpression('/^[a-f0-9-]+\.ics$/'),
$result,
CalDavBackend::CALENDAR_TYPE_SUBSCRIPTION
);
$refreshWebcalService->refreshSubscription('principals/users/testuser', 'sub123');
$refreshWebcalService->refreshSubscription('principals/users/testuser', 'sub123');
}
}
#[\PHPUnit\Framework\Attributes\DataProvider('identicalDataProvider')]
#[\PHPUnit\Framework\Attributes\DataProvider('identicalDataProvider')]
public function testRunIdentical(string $uid, array $calendarObject, string $body, string $contentType, string $result): void {
public function testRunIdentical(string $uid, array $calendarObject, string $body, string $format, string $result): void {
$refreshWebcalService = $this->getMockBuilder(RefreshWebcalService::class)
$refreshWebcalService = new RefreshWebcalService(
->onlyMethods(['getRandomCalendarObjectUri'])
$this->caldavBackend,
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->time])
$this->logger,
->getMock();
$this->connection,
$this->timeFactory,
$refreshWebcalService
$this->importService
->method('getRandomCalendarObjectUri')
);
->willReturn('uri-1.ics');
$this->caldavBackend->expects(self::once())
$this->caldavBackend->expects(self::once())
->method('getSubscriptionsForUser')
->method('getSubscriptionsForUser')
@ -118,78 +155,199 @@ class RefreshWebcalServiceTest extends TestCase {
],
],
]);
]);
$stream = $this->createStreamFromString($body);
$this->connection->expects(self::once())
$this->connection->expects(self::once())
->method('queryWebcalFeed')
->method('queryWebcalFeed')
->willReturn($result );
->willReturn(['data' => $stream, 'format' => $format] );
$this->caldavBackend->expects(self::once())
$this->caldavBackend->expects(self::once())
->method('getLimitedCalendarObjects')
->method('getLimitedCalendarObjects')
->willReturn($calendarObject);
->willReturn($calendarObject);
$denormalised = [
// Create a VCalendar object that will be yielded by the import service
'etag' => 100,
$vCalendar = VObject\Reader::read($result);
'size' => strlen($calendarObject[$uid]['calendardata']),
'uid' => 'sub456'
];
$this->caldavBackend->expects(self::once())
$generator = function () use ($vCalendar) {
->method('getDenormalizedData')
yield $vCalendar;
->willReturn($denormalised);
};
$this->importService->expects(self::once())
->method('importText')
->willReturn($generator());
$this->caldavBackend->expects(self::never())
$this->caldavBackend->expects(self::never())
->method('createCalendarObject');
->method('createCalendarObject');
$refreshWebcalService->refreshSubscription('principals/users/testuser', 'sub456 ');
$refreshWebcalService->refreshSubscription('principals/users/testuser', 'sub123 ');
}
}
public function testRunJustUpdated(): void {
public function testSubscriptionNotFound(): void {
$refreshWebcalService = $this->getMockBuilder(RefreshWebcalService::class)
$refreshWebcalService = new RefreshWebcalService(
->onlyMethods(['getRandomCalendarObjectUri'])
$this->caldavBackend,
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->time])
$this->logger,
->getMock();
$this->connection,
$this->timeFactory,
$this->importService
);
$refreshWebcalService
$this->caldavBackend->expects(self::once())
->method('getRandomCalendarObjectUri')
->method('getSubscriptionsForUser')
->willReturn('uri-1.ics');
->with('principals/users/testuser')
->willReturn([]);
$this->connection->expects(self::never())
->method('queryWebcalFeed');
$refreshWebcalService->refreshSubscription('principals/users/testuser', 'sub123');
}
public function testConnectionReturnsNull(): void {
$refreshWebcalService = new RefreshWebcalService(
$this->caldavBackend,
$this->logger,
$this->connection,
$this->timeFactory,
$this->importService
);
$this->caldavBackend->expects(self::once())
$this->caldavBackend->expects(self::once())
->method('getSubscriptionsForUser')
->method('getSubscriptionsForUser')
->with('principals/users/testuser')
->with('principals/users/testuser')
->willReturn([
->willReturn([
[
[
'id' => '99',
'id' => '42',
'uri' => 'sub456',
'uri' => 'sub123',
RefreshWebcalService::REFRESH_RATE => 'P1D',
RefreshWebcalService::STRIP_TODOS => '1',
RefreshWebcalService::STRIP_TODOS => '1',
RefreshWebcalService::STRIP_ALARMS => '1',
RefreshWebcalService::STRIP_ALARMS => '1',
RefreshWebcalService::STRIP_ATTACHMENTS => '1',
RefreshWebcalService::STRIP_ATTACHMENTS => '1',
'source' => 'webcal://foo.bar/bla',
'source' => 'webcal://foo.bar/bla2 ',
'lastmodified' => time(),
'lastmodified' => 0 ,
],
],
]);
$this->connection->expects(self::once())
->method('queryWebcalFeed')
->willReturn(null);
$this->importService->expects(self::never())
->method('importText');
$this->caldavBackend->expects(self::never())
->method('createCalendarObject');
$refreshWebcalService->refreshSubscription('principals/users/testuser', 'sub123');
}
public function testDeletedObjectsArePurged(): void {
$refreshWebcalService = new RefreshWebcalService(
$this->caldavBackend,
$this->logger,
$this->connection,
$this->timeFactory,
$this->importService
);
$this->caldavBackend->expects(self::once())
->method('getSubscriptionsForUser')
->with('principals/users/testuser')
->willReturn([
[
[
'id' => '42',
'id' => '42',
'uri' => 'sub123',
'uri' => 'sub123',
RefreshWebcalService::REFRESH_RATE => 'PT1H',
RefreshWebcalService::STRIP_TODOS => '1',
RefreshWebcalService::STRIP_TODOS => '1',
RefreshWebcalService::STRIP_ALARMS => '1',
RefreshWebcalService::STRIP_ALARMS => '1',
RefreshWebcalService::STRIP_ATTACHMENTS => '1',
RefreshWebcalService::STRIP_ATTACHMENTS => '1',
'source' => 'webcal://foo.bar/bla2',
'source' => 'webcal://foo.bar/bla2',
'lastmodified' => time(),
'lastmodified' => 0 ,
],
],
]);
]);
$timeMock = $this->createMock(\DateTime::class);
$body = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Test//Test//EN\r\nBEGIN:VEVENT\r\nUID:new-event\r\nDTSTAMP:20160218T133704Z\r\nDTSTART:20160218T133704Z\r\nSUMMARY:New Event\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$this->time->expects(self::once())
$stream = $this->createStreamFromString($body);
->method('getDateTime')
->willReturn($timeMock);
$this->connection->expects(self::once())
$timeMock->expects(self::once())
->method('queryWebcalFeed')
->method('getTimestamp')
->willReturn(['data' => $stream, 'format' => 'ical']);
->willReturn(2101724667);
$this->time->expects(self::once())
// Existing objects include one that won't be in the feed
->method('getTime')
$this->caldavBackend->expects(self::once())
->willReturn(time());
->method('getLimitedCalendarObjects')
$this->connection->expects(self::never())
->willReturn([
->method('queryWebcalFeed');
'old-deleted-event' => [
'id' => 99,
'uid' => 'old-deleted-event',
'etag' => 'old-etag',
'uri' => 'old-event.ics',
],
]);
$vCalendar = VObject\Reader::read($body);
$generator = function () use ($vCalendar) {
yield $vCalendar;
};
$this->importService->expects(self::once())
->method('importText')
->willReturn($generator());
$this->caldavBackend->expects(self::once())
->method('createCalendarObject');
$this->caldavBackend->expects(self::once())
->method('purgeCachedEventsForSubscription')
->with(42, [99], ['old-event.ics']);
$refreshWebcalService->refreshSubscription('principals/users/testuser', 'sub123');
}
public function testLongUidIsSkipped(): void {
$refreshWebcalService = new RefreshWebcalService(
$this->caldavBackend,
$this->logger,
$this->connection,
$this->timeFactory,
$this->importService
);
$this->caldavBackend->expects(self::once())
->method('getSubscriptionsForUser')
->with('principals/users/testuser')
->willReturn([
[
'id' => '42',
'uri' => 'sub123',
RefreshWebcalService::STRIP_TODOS => '1',
RefreshWebcalService::STRIP_ALARMS => '1',
RefreshWebcalService::STRIP_ATTACHMENTS => '1',
'source' => 'webcal://foo.bar/bla2',
'lastmodified' => 0,
],
]);
// Create a UID that is longer than 512 characters
$longUid = str_repeat('a', 513);
$body = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Test//Test//EN\r\nBEGIN:VEVENT\r\nUID:$longUid\r\nDTSTAMP:20160218T133704Z\r\nDTSTART:20160218T133704Z\r\nSUMMARY:Event with long UID\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$stream = $this->createStreamFromString($body);
$this->connection->expects(self::once())
->method('queryWebcalFeed')
->willReturn(['data' => $stream, 'format' => 'ical']);
$this->caldavBackend->expects(self::once())
->method('getLimitedCalendarObjects')
->willReturn([]);
$vCalendar = VObject\Reader::read($body);
$generator = function () use ($vCalendar) {
yield $vCalendar;
};
$this->importService->expects(self::once())
->method('importText')
->willReturn($generator());
// Event with long UID should be skipped, so createCalendarObject should never be called
$this->caldavBackend->expects(self::never())
$this->caldavBackend->expects(self::never())
->method('createCalendarObject');
->method('createCalendarObject');
@ -197,16 +355,12 @@ class RefreshWebcalServiceTest extends TestCase {
}
}
#[\PHPUnit\Framework\Attributes\DataProvider('runDataProvider')]
#[\PHPUnit\Framework\Attributes\DataProvider('runDataProvider')]
public function testRunCreateCalendarNoException(string $body, string $contentType , string $result): void {
public function testRunCreateCalendarNoException(string $body, string $format , string $result): void {
$refreshWebcalService = $this->getMockBuilder(RefreshWebcalService::class)
$refreshWebcalService = $this->getMockBuilder(RefreshWebcalService::class)
->onlyMethods(['getRandomCalendarObjectUri', 'get Subscription', ])
->onlyMethods(['getSubscription'])
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->time])
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->timeFactory, $this->importService ])
->getMock();
->getMock();
$refreshWebcalService
->method('getRandomCalendarObjectUri')
->willReturn('uri-1.ics');
$refreshWebcalService
$refreshWebcalService
->method('getSubscription')
->method('getSubscription')
->willReturn([
->willReturn([
@ -220,13 +374,26 @@ class RefreshWebcalServiceTest extends TestCase {
'lastmodified' => 0,
'lastmodified' => 0,
]);
]);
$stream = $this->createStreamFromString($body);
$this->connection->expects(self::once())
$this->connection->expects(self::once())
->method('queryWebcalFeed')
->method('queryWebcalFeed')
->willReturn($result );
->willReturn(['data' => $stream, 'format' => $format] );
$this->caldavBackend->expects(self::once())
$this->caldavBackend->expects(self::once())
->method('createCalendarObject')
->method('getLimitedCalendarObjects')
->with(42, 'uri-1.ics', $result, 1);
->willReturn([]);
// Create a VCalendar object that will be yielded by the import service
$vCalendar = VObject\Reader::read($result);
$generator = function () use ($vCalendar) {
yield $vCalendar;
};
$this->importService->expects(self::once())
->method('importText')
->willReturn($generator());
$noInstanceException = new NoInstancesException("can't add calendar object");
$noInstanceException = new NoInstancesException("can't add calendar object");
$this->caldavBackend->expects(self::once())
$this->caldavBackend->expects(self::once())
@ -241,16 +408,12 @@ class RefreshWebcalServiceTest extends TestCase {
}
}
#[\PHPUnit\Framework\Attributes\DataProvider('runDataProvider')]
#[\PHPUnit\Framework\Attributes\DataProvider('runDataProvider')]
public function testRunCreateCalendarBadRequest(string $body, string $contentType , string $result): void {
public function testRunCreateCalendarBadRequest(string $body, string $format , string $result): void {
$refreshWebcalService = $this->getMockBuilder(RefreshWebcalService::class)
$refreshWebcalService = $this->getMockBuilder(RefreshWebcalService::class)
->onlyMethods(['getRandomCalendarObjectUri', 'get Subscription'])
->onlyMethods(['getSubscription'])
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->time])
->setConstructorArgs([$this->caldavBackend, $this->logger, $this->connection, $this->timeFactory, $this->importService ])
->getMock();
->getMock();
$refreshWebcalService
->method('getRandomCalendarObjectUri')
->willReturn('uri-1.ics');
$refreshWebcalService
$refreshWebcalService
->method('getSubscription')
->method('getSubscription')
->willReturn([
->willReturn([
@ -264,13 +427,26 @@ class RefreshWebcalServiceTest extends TestCase {
'lastmodified' => 0,
'lastmodified' => 0,
]);
]);
$stream = $this->createStreamFromString($body);
$this->connection->expects(self::once())
$this->connection->expects(self::once())
->method('queryWebcalFeed')
->method('queryWebcalFeed')
->willReturn($result );
->willReturn(['data' => $stream, 'format' => $format] );
$this->caldavBackend->expects(self::once())
$this->caldavBackend->expects(self::once())
->method('createCalendarObject')
->method('getLimitedCalendarObjects')
->with(42, 'uri-1.ics', $result, 1);
->willReturn([]);
// Create a VCalendar object that will be yielded by the import service
$vCalendar = VObject\Reader::read($result);
$generator = function () use ($vCalendar) {
yield $vCalendar;
};
$this->importService->expects(self::once())
->method('importText')
->willReturn($generator());
$badRequestException = new BadRequest("can't add reach calendar url");
$badRequestException = new BadRequest("can't add reach calendar url");
$this->caldavBackend->expects(self::once())
$this->caldavBackend->expects(self::once())
@ -285,20 +461,22 @@ class RefreshWebcalServiceTest extends TestCase {
}
}
public static function identicalDataProvider(): array {
public static function identicalDataProvider(): array {
$icalBody = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject " . VObject\Version::VERSION . "//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
$etag = md5($icalBody);
return [
return [
[
[
'12345',
'12345',
[
[
'12345' => [
'12345' => [
'id' => 42,
'id' => 42,
'etag' => 100,
'etag' => $etag,
'uri' => 'sub456',
'uri' => 'sub456.ics',
'calendardata' => "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
],
],
],
],
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
'text/calendar;charset=utf8 ',
'ical ',
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20180218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n" ,
$icalBody ,
],
],
];
];
}
}
@ -307,19 +485,9 @@ class RefreshWebcalServiceTest extends TestCase {
return [
return [
[
[
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
'text/calendar;charset=utf8 ',
'ical ',
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject " . VObject\Version::VERSION . "//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject " . VObject\Version::VERSION . "//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nUID:12345\r\nDTSTAMP:20160218T133704Z\r\nDTSTART;VALUE=DATE:19000101\r\nDTEND;VALUE=DATE:19000102\r\nRRULE:FREQ=YEARLY\r\nSUMMARY:12345's Birthday (1900)\r\nTRANSP:TRANSPARENT\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n",
],
],
[
'["vcalendar",[["prodid",{},"text","-//Example Corp.//Example Client//EN"],["version",{},"text","2.0"]],[["vtimezone",[["last-modified",{},"date-time","2004-01-10T03:28:45Z"],["tzid",{},"text","US/Eastern"]],[["daylight",[["dtstart",{},"date-time","2000-04-04T02:00:00"],["rrule",{},"recur",{"freq":"YEARLY","byday":"1SU","bymonth":4}],["tzname",{},"text","EDT"],["tzoffsetfrom",{},"utc-offset","-05:00"],["tzoffsetto",{},"utc-offset","-04:00"]],[]],["standard",[["dtstart",{},"date-time","2000-10-26T02:00:00"],["rrule",{},"recur",{"freq":"YEARLY","byday":"1SU","bymonth":10}],["tzname",{},"text","EST"],["tzoffsetfrom",{},"utc-offset","-04:00"],["tzoffsetto",{},"utc-offset","-05:00"]],[]]]],["vevent",[["dtstamp",{},"date-time","2006-02-06T00:11:21Z"],["dtstart",{"tzid":"US/Eastern"},"date-time","2006-01-02T14:00:00"],["duration",{},"duration","PT1H"],["recurrence-id",{"tzid":"US/Eastern"},"date-time","2006-01-04T12:00:00"],["summary",{},"text","Event #2"],["uid",{},"text","12345"]],[]]]]',
'application/calendar+json',
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject " . VObject\Version::VERSION . "//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VTIMEZONE\r\nLAST-MODIFIED:20040110T032845Z\r\nTZID:US/Eastern\r\nBEGIN:DAYLIGHT\r\nDTSTART:20000404T020000\r\nRRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4\r\nTZNAME:EDT\r\nTZOFFSETFROM:-0500\r\nTZOFFSETTO:-0400\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nDTSTART:20001026T020000\r\nRRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=10\r\nTZNAME:EST\r\nTZOFFSETFROM:-0400\r\nTZOFFSETTO:-0500\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTAMP:20060206T001121Z\r\nDTSTART;TZID=US/Eastern:20060102T140000\r\nDURATION:PT1H\r\nRECURRENCE-ID;TZID=US/Eastern:20060104T120000\r\nSUMMARY:Event #2\r\nUID:12345\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"
],
[
'<? xml version = "1.0" encoding = "utf-8" ?> < icalendar xmlns = "urn:ietf:params:xml:ns:icalendar-2.0" >< vcalendar >< properties >< prodid >< text > -//Example Inc.//Example Client//EN</ text ></ prodid >< version >< text > 2.0</ text ></ version ></ properties >< components >< vevent >< properties >< dtstamp >< date-time > 2006-02-06T00:11:21Z</ date-time ></ dtstamp >< dtstart >< parameters >< tzid >< text > US/Eastern</ text ></ tzid ></ parameters >< date-time > 2006-01-04T14:00:00</ date-time ></ dtstart >< duration >< duration > PT1H</ duration ></ duration >< recurrence-id >< parameters >< tzid >< text > US/Eastern</ text ></ tzid ></ parameters >< date-time > 2006-01-04T12:00:00</ date-time ></ recurrence-id >< summary >< text > Event #2 bis</ text ></ summary >< uid >< text > 12345</ text ></ uid ></ properties ></ vevent ></ components ></ vcalendar ></ icalendar > ',
'application/calendar+xml',
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Sabre//Sabre VObject " . VObject\Version::VERSION . "//EN\r\nCALSCALE:GREGORIAN\r\nBEGIN:VEVENT\r\nDTSTAMP:20060206T001121Z\r\nDTSTART;TZID=US/Eastern:20060104T140000\r\nDURATION:PT1H\r\nRECURRENCE-ID;TZID=US/Eastern:20060104T120000\r\nSUMMARY:Event #2 bis\r\nUID:12345\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"
]
];
];
}
}
}
}