1<?php 2 3namespace Sabre\VObject\Component; 4 5use DateTimeZone; 6use Sabre\VObject; 7 8class VCalendarTest extends \PHPUnit_Framework_TestCase { 9 10 use VObject\PHPUnitAssertions; 11 12 /** 13 * @dataProvider expandData 14 */ 15 function testExpand($input, $output, $timeZone = 'UTC', $start = '2011-12-01', $end = '2011-12-31') { 16 17 $vcal = VObject\Reader::read($input); 18 19 $timeZone = new DateTimeZone($timeZone); 20 21 $vcal = $vcal->expand( 22 new \DateTime($start), 23 new \DateTime($end), 24 $timeZone 25 ); 26 27 // This will normalize the output 28 $output = VObject\Reader::read($output)->serialize(); 29 30 $this->assertVObjectEqualsVObject($output, $vcal->serialize()); 31 32 } 33 34 function expandData() { 35 36 $tests = []; 37 38 // No data 39 $input = 'BEGIN:VCALENDAR 40CALSCALE:GREGORIAN 41VERSION:2.0 42END:VCALENDAR 43'; 44 45 $output = $input; 46 $tests[] = [$input,$output]; 47 48 49 // Simple events 50 $input = 'BEGIN:VCALENDAR 51CALSCALE:GREGORIAN 52VERSION:2.0 53BEGIN:VEVENT 54UID:bla 55SUMMARY:InExpand 56DTSTART;VALUE=DATE:20111202 57END:VEVENT 58BEGIN:VEVENT 59UID:bla2 60SUMMARY:NotInExpand 61DTSTART;VALUE=DATE:20120101 62END:VEVENT 63END:VCALENDAR 64'; 65 66 $output = 'BEGIN:VCALENDAR 67CALSCALE:GREGORIAN 68VERSION:2.0 69BEGIN:VEVENT 70UID:bla 71SUMMARY:InExpand 72DTSTART;VALUE=DATE:20111202 73END:VEVENT 74END:VCALENDAR 75'; 76 77 $tests[] = [$input, $output]; 78 79 // Removing timezone info 80 $input = 'BEGIN:VCALENDAR 81CALSCALE:GREGORIAN 82VERSION:2.0 83BEGIN:VTIMEZONE 84TZID:Europe/Paris 85END:VTIMEZONE 86BEGIN:VEVENT 87UID:bla4 88SUMMARY:RemoveTZ info 89DTSTART;TZID=Europe/Paris:20111203T130102 90END:VEVENT 91END:VCALENDAR 92'; 93 94 $output = 'BEGIN:VCALENDAR 95CALSCALE:GREGORIAN 96VERSION:2.0 97BEGIN:VEVENT 98UID:bla4 99SUMMARY:RemoveTZ info 100DTSTART:20111203T120102Z 101END:VEVENT 102END:VCALENDAR 103'; 104 105 $tests[] = [$input, $output]; 106 107 // Removing timezone info from sub-components. See Issue #278 108 $input = 'BEGIN:VCALENDAR 109CALSCALE:GREGORIAN 110VERSION:2.0 111BEGIN:VTIMEZONE 112TZID:Europe/Paris 113END:VTIMEZONE 114BEGIN:VEVENT 115UID:bla4 116SUMMARY:RemoveTZ info 117DTSTART;TZID=Europe/Paris:20111203T130102 118BEGIN:VALARM 119TRIGGER;VALUE=DATE-TIME;TZID=America/New_York:20151209T133200 120END:VALARM 121END:VEVENT 122END:VCALENDAR 123'; 124 125 $output = 'BEGIN:VCALENDAR 126CALSCALE:GREGORIAN 127VERSION:2.0 128BEGIN:VEVENT 129UID:bla4 130SUMMARY:RemoveTZ info 131DTSTART:20111203T120102Z 132BEGIN:VALARM 133TRIGGER;VALUE=DATE-TIME:20151209T183200Z 134END:VALARM 135END:VEVENT 136END:VCALENDAR 137'; 138 139 $tests[] = [$input, $output]; 140 141 // Recurrence rule 142 $input = 'BEGIN:VCALENDAR 143CALSCALE:GREGORIAN 144VERSION:2.0 145BEGIN:VEVENT 146UID:bla6 147SUMMARY:Testing RRule 148DTSTART:20111125T120000Z 149DTEND:20111125T130000Z 150RRULE:FREQ=WEEKLY 151END:VEVENT 152END:VCALENDAR 153'; 154 155 $output = 'BEGIN:VCALENDAR 156CALSCALE:GREGORIAN 157VERSION:2.0 158BEGIN:VEVENT 159UID:bla6 160SUMMARY:Testing RRule 161DTSTART:20111202T120000Z 162DTEND:20111202T130000Z 163RECURRENCE-ID:20111202T120000Z 164END:VEVENT 165BEGIN:VEVENT 166UID:bla6 167SUMMARY:Testing RRule 168DTSTART:20111209T120000Z 169DTEND:20111209T130000Z 170RECURRENCE-ID:20111209T120000Z 171END:VEVENT 172BEGIN:VEVENT 173UID:bla6 174SUMMARY:Testing RRule 175DTSTART:20111216T120000Z 176DTEND:20111216T130000Z 177RECURRENCE-ID:20111216T120000Z 178END:VEVENT 179BEGIN:VEVENT 180UID:bla6 181SUMMARY:Testing RRule 182DTSTART:20111223T120000Z 183DTEND:20111223T130000Z 184RECURRENCE-ID:20111223T120000Z 185END:VEVENT 186BEGIN:VEVENT 187UID:bla6 188SUMMARY:Testing RRule 189DTSTART:20111230T120000Z 190DTEND:20111230T130000Z 191RECURRENCE-ID:20111230T120000Z 192END:VEVENT 193END:VCALENDAR 194'; 195 196 $tests[] = [$input, $output]; 197 198 // Recurrence rule + override 199 $input = 'BEGIN:VCALENDAR 200CALSCALE:GREGORIAN 201VERSION:2.0 202BEGIN:VEVENT 203UID:bla6 204SUMMARY:Testing RRule2 205DTSTART:20111125T120000Z 206DTEND:20111125T130000Z 207RRULE:FREQ=WEEKLY 208END:VEVENT 209BEGIN:VEVENT 210UID:bla6 211RECURRENCE-ID:20111209T120000Z 212DTSTART:20111209T140000Z 213DTEND:20111209T150000Z 214SUMMARY:Override! 215END:VEVENT 216END:VCALENDAR 217'; 218 219 $output = 'BEGIN:VCALENDAR 220CALSCALE:GREGORIAN 221VERSION:2.0 222BEGIN:VEVENT 223UID:bla6 224SUMMARY:Testing RRule2 225DTSTART:20111202T120000Z 226DTEND:20111202T130000Z 227RECURRENCE-ID:20111202T120000Z 228END:VEVENT 229BEGIN:VEVENT 230UID:bla6 231RECURRENCE-ID:20111209T120000Z 232DTSTART:20111209T140000Z 233DTEND:20111209T150000Z 234SUMMARY:Override! 235END:VEVENT 236BEGIN:VEVENT 237UID:bla6 238SUMMARY:Testing RRule2 239DTSTART:20111216T120000Z 240DTEND:20111216T130000Z 241RECURRENCE-ID:20111216T120000Z 242END:VEVENT 243BEGIN:VEVENT 244UID:bla6 245SUMMARY:Testing RRule2 246DTSTART:20111223T120000Z 247DTEND:20111223T130000Z 248RECURRENCE-ID:20111223T120000Z 249END:VEVENT 250BEGIN:VEVENT 251UID:bla6 252SUMMARY:Testing RRule2 253DTSTART:20111230T120000Z 254DTEND:20111230T130000Z 255RECURRENCE-ID:20111230T120000Z 256END:VEVENT 257END:VCALENDAR 258'; 259 260 $tests[] = [$input, $output]; 261 262 // Floating dates and times. 263 $input = <<<ICS 264BEGIN:VCALENDAR 265VERSION:2.0 266BEGIN:VEVENT 267UID:bla1 268DTSTART:20141112T195000 269END:VEVENT 270BEGIN:VEVENT 271UID:bla2 272DTSTART;VALUE=DATE:20141112 273END:VEVENT 274BEGIN:VEVENT 275UID:bla3 276DTSTART;VALUE=DATE:20141112 277RRULE:FREQ=DAILY;COUNT=2 278END:VEVENT 279END:VCALENDAR 280ICS; 281 282 $output = <<<ICS 283BEGIN:VCALENDAR 284VERSION:2.0 285BEGIN:VEVENT 286UID:bla1 287DTSTART:20141112T225000Z 288END:VEVENT 289BEGIN:VEVENT 290UID:bla2 291DTSTART;VALUE=DATE:20141112 292END:VEVENT 293BEGIN:VEVENT 294UID:bla3 295DTSTART;VALUE=DATE:20141112 296RECURRENCE-ID;VALUE=DATE:20141112 297END:VEVENT 298BEGIN:VEVENT 299UID:bla3 300DTSTART;VALUE=DATE:20141113 301RECURRENCE-ID;VALUE=DATE:20141113 302END:VEVENT 303END:VCALENDAR 304ICS; 305 306 $tests[] = [$input, $output, 'America/Argentina/Buenos_Aires', '2014-01-01', '2015-01-01']; 307 308 // Recurrence rule with no valid instances 309 $input = 'BEGIN:VCALENDAR 310CALSCALE:GREGORIAN 311VERSION:2.0 312BEGIN:VEVENT 313UID:bla6 314SUMMARY:Testing RRule3 315DTSTART:20111125T120000Z 316DTEND:20111125T130000Z 317RRULE:FREQ=WEEKLY;COUNT=1 318EXDATE:20111125T120000Z 319END:VEVENT 320END:VCALENDAR 321'; 322 323 $output = 'BEGIN:VCALENDAR 324CALSCALE:GREGORIAN 325VERSION:2.0 326END:VCALENDAR 327'; 328 329 $tests[] = [$input, $output]; 330 return $tests; 331 332 } 333 334 /** 335 * @expectedException \Sabre\VObject\InvalidDataException 336 */ 337 function testBrokenEventExpand() { 338 339 $input = 'BEGIN:VCALENDAR 340CALSCALE:GREGORIAN 341VERSION:2.0 342BEGIN:VEVENT 343RRULE:FREQ=WEEKLY 344DTSTART;VALUE=DATE:20111202 345END:VEVENT 346END:VCALENDAR 347'; 348 $vcal = VObject\Reader::read($input); 349 $vcal->expand( 350 new \DateTime('2011-12-01'), 351 new \DateTime('2011-12-31') 352 ); 353 354 } 355 356 function testGetDocumentType() { 357 358 $vcard = new VCalendar(); 359 $vcard->VERSION = '2.0'; 360 $this->assertEquals(VCalendar::ICALENDAR20, $vcard->getDocumentType()); 361 362 } 363 364 function testValidateCorrect() { 365 366 $input = 'BEGIN:VCALENDAR 367CALSCALE:GREGORIAN 368VERSION:2.0 369PRODID:foo 370BEGIN:VEVENT 371DTSTART;VALUE=DATE:20111202 372DTSTAMP:20140122T233226Z 373UID:foo 374END:VEVENT 375END:VCALENDAR 376'; 377 378 $vcal = VObject\Reader::read($input); 379 $this->assertEquals([], $vcal->validate(), 'Got an error'); 380 381 } 382 383 function testValidateNoVersion() { 384 385 $input = 'BEGIN:VCALENDAR 386CALSCALE:GREGORIAN 387PRODID:foo 388BEGIN:VEVENT 389DTSTART;VALUE=DATE:20111202 390UID:foo 391DTSTAMP:20140122T234434Z 392END:VEVENT 393END:VCALENDAR 394'; 395 396 $vcal = VObject\Reader::read($input); 397 $this->assertEquals(1, count($vcal->validate())); 398 399 } 400 401 function testValidateWrongVersion() { 402 403 $input = 'BEGIN:VCALENDAR 404CALSCALE:GREGORIAN 405VERSION:3.0 406PRODID:foo 407BEGIN:VEVENT 408DTSTART;VALUE=DATE:20111202 409UID:foo 410DTSTAMP:20140122T234434Z 411END:VEVENT 412END:VCALENDAR 413'; 414 415 $vcal = VObject\Reader::read($input); 416 $this->assertEquals(1, count($vcal->validate())); 417 418 } 419 420 function testValidateNoProdId() { 421 422 $input = 'BEGIN:VCALENDAR 423CALSCALE:GREGORIAN 424VERSION:2.0 425BEGIN:VEVENT 426DTSTART;VALUE=DATE:20111202 427UID:foo 428DTSTAMP:20140122T234434Z 429END:VEVENT 430END:VCALENDAR 431'; 432 433 $vcal = VObject\Reader::read($input); 434 $this->assertEquals(1, count($vcal->validate())); 435 436 } 437 438 function testValidateDoubleCalScale() { 439 440 $input = 'BEGIN:VCALENDAR 441VERSION:2.0 442PRODID:foo 443CALSCALE:GREGORIAN 444CALSCALE:GREGORIAN 445BEGIN:VEVENT 446DTSTART;VALUE=DATE:20111202 447UID:foo 448DTSTAMP:20140122T234434Z 449END:VEVENT 450END:VCALENDAR 451'; 452 453 $vcal = VObject\Reader::read($input); 454 $this->assertEquals(1, count($vcal->validate())); 455 456 } 457 458 function testValidateDoubleMethod() { 459 460 $input = 'BEGIN:VCALENDAR 461VERSION:2.0 462PRODID:foo 463METHOD:REQUEST 464METHOD:REQUEST 465BEGIN:VEVENT 466DTSTART;VALUE=DATE:20111202 467UID:foo 468DTSTAMP:20140122T234434Z 469END:VEVENT 470END:VCALENDAR 471'; 472 473 $vcal = VObject\Reader::read($input); 474 $this->assertEquals(1, count($vcal->validate())); 475 476 } 477 478 function testValidateTwoMasterEvents() { 479 480 $input = 'BEGIN:VCALENDAR 481VERSION:2.0 482PRODID:foo 483METHOD:REQUEST 484BEGIN:VEVENT 485DTSTART;VALUE=DATE:20111202 486UID:foo 487DTSTAMP:20140122T234434Z 488END:VEVENT 489BEGIN:VEVENT 490DTSTART;VALUE=DATE:20111202 491UID:foo 492DTSTAMP:20140122T234434Z 493END:VEVENT 494END:VCALENDAR 495'; 496 497 $vcal = VObject\Reader::read($input); 498 $this->assertEquals(1, count($vcal->validate())); 499 500 } 501 502 function testValidateOneMasterEvent() { 503 504 $input = 'BEGIN:VCALENDAR 505VERSION:2.0 506PRODID:foo 507METHOD:REQUEST 508BEGIN:VEVENT 509DTSTART;VALUE=DATE:20111202 510UID:foo 511DTSTAMP:20140122T234434Z 512END:VEVENT 513BEGIN:VEVENT 514DTSTART;VALUE=DATE:20111202 515UID:foo 516DTSTAMP:20140122T234434Z 517RECURRENCE-ID;VALUE=DATE:20111202 518END:VEVENT 519END:VCALENDAR 520'; 521 522 $vcal = VObject\Reader::read($input); 523 $this->assertEquals(0, count($vcal->validate())); 524 525 } 526 527 function testGetBaseComponent() { 528 529 $input = 'BEGIN:VCALENDAR 530VERSION:2.0 531PRODID:foo 532METHOD:REQUEST 533BEGIN:VEVENT 534SUMMARY:test 535DTSTART;VALUE=DATE:20111202 536UID:foo 537DTSTAMP:20140122T234434Z 538END:VEVENT 539BEGIN:VEVENT 540DTSTART;VALUE=DATE:20111202 541UID:foo 542DTSTAMP:20140122T234434Z 543RECURRENCE-ID;VALUE=DATE:20111202 544END:VEVENT 545END:VCALENDAR 546'; 547 548 $vcal = VObject\Reader::read($input); 549 550 $result = $vcal->getBaseComponent(); 551 $this->assertEquals('test', $result->SUMMARY->getValue()); 552 553 } 554 555 function testGetBaseComponentNoResult() { 556 557 $input = 'BEGIN:VCALENDAR 558VERSION:2.0 559PRODID:foo 560METHOD:REQUEST 561BEGIN:VEVENT 562SUMMARY:test 563RECURRENCE-ID;VALUE=DATE:20111202 564DTSTART;VALUE=DATE:20111202 565UID:foo 566DTSTAMP:20140122T234434Z 567END:VEVENT 568BEGIN:VEVENT 569DTSTART;VALUE=DATE:20111202 570UID:foo 571DTSTAMP:20140122T234434Z 572RECURRENCE-ID;VALUE=DATE:20111202 573END:VEVENT 574END:VCALENDAR 575'; 576 577 $vcal = VObject\Reader::read($input); 578 579 $result = $vcal->getBaseComponent(); 580 $this->assertNull($result); 581 582 } 583 584 function testGetBaseComponentWithFilter() { 585 586 $input = 'BEGIN:VCALENDAR 587VERSION:2.0 588PRODID:foo 589METHOD:REQUEST 590BEGIN:VEVENT 591SUMMARY:test 592DTSTART;VALUE=DATE:20111202 593UID:foo 594DTSTAMP:20140122T234434Z 595END:VEVENT 596BEGIN:VEVENT 597DTSTART;VALUE=DATE:20111202 598UID:foo 599DTSTAMP:20140122T234434Z 600RECURRENCE-ID;VALUE=DATE:20111202 601END:VEVENT 602END:VCALENDAR 603'; 604 605 $vcal = VObject\Reader::read($input); 606 607 $result = $vcal->getBaseComponent('VEVENT'); 608 $this->assertEquals('test', $result->SUMMARY->getValue()); 609 610 } 611 612 function testGetBaseComponentWithFilterNoResult() { 613 614 $input = 'BEGIN:VCALENDAR 615VERSION:2.0 616PRODID:foo 617METHOD:REQUEST 618BEGIN:VTODO 619SUMMARY:test 620UID:foo 621DTSTAMP:20140122T234434Z 622END:VTODO 623END:VCALENDAR 624'; 625 626 $vcal = VObject\Reader::read($input); 627 628 $result = $vcal->getBaseComponent('VEVENT'); 629 $this->assertNull($result); 630 631 } 632 633 function testNoComponents() { 634 635 $input = <<<ICS 636BEGIN:VCALENDAR 637VERSION:2.0 638PRODID:vobject 639END:VCALENDAR 640ICS; 641 642 $this->assertValidate( 643 $input, 644 0, 645 3, 646 "An iCalendar object must have at least 1 component." 647 ); 648 649 } 650 651 function testCalDAVNoComponents() { 652 653 $input = <<<ICS 654BEGIN:VCALENDAR 655VERSION:2.0 656PRODID:vobject 657BEGIN:VTIMEZONE 658TZID:America/Toronto 659END:VTIMEZONE 660END:VCALENDAR 661ICS; 662 663 $this->assertValidate( 664 $input, 665 VCalendar::PROFILE_CALDAV, 666 3, 667 "A calendar object on a CalDAV server must have at least 1 component (VTODO, VEVENT, VJOURNAL)." 668 ); 669 670 } 671 672 function testCalDAVMultiUID() { 673 674 $input = <<<ICS 675BEGIN:VCALENDAR 676VERSION:2.0 677PRODID:vobject 678BEGIN:VEVENT 679UID:foo 680DTSTAMP:20150109T184500Z 681DTSTART:20150109T184500Z 682END:VEVENT 683BEGIN:VEVENT 684UID:bar 685DTSTAMP:20150109T184500Z 686DTSTART:20150109T184500Z 687END:VEVENT 688END:VCALENDAR 689ICS; 690 691 $this->assertValidate( 692 $input, 693 VCalendar::PROFILE_CALDAV, 694 3, 695 "A calendar object on a CalDAV server may only have components with the same UID." 696 ); 697 698 } 699 700 function testCalDAVMultiComponent() { 701 702 $input = <<<ICS 703BEGIN:VCALENDAR 704VERSION:2.0 705PRODID:vobject 706BEGIN:VEVENT 707UID:foo 708RECURRENCE-ID:20150109T185200Z 709DTSTAMP:20150109T184500Z 710DTSTART:20150109T184500Z 711END:VEVENT 712BEGIN:VTODO 713UID:foo 714DTSTAMP:20150109T184500Z 715DTSTART:20150109T184500Z 716END:VTODO 717END:VCALENDAR 718ICS; 719 720 $this->assertValidate( 721 $input, 722 VCalendar::PROFILE_CALDAV, 723 3, 724 "A calendar object on a CalDAV server may only have 1 type of component (VEVENT, VTODO or VJOURNAL)." 725 ); 726 727 } 728 729 function testCalDAVMETHOD() { 730 731 $input = <<<ICS 732BEGIN:VCALENDAR 733VERSION:2.0 734METHOD:PUBLISH 735PRODID:vobject 736BEGIN:VEVENT 737UID:foo 738RECURRENCE-ID:20150109T185200Z 739DTSTAMP:20150109T184500Z 740DTSTART:20150109T184500Z 741END:VEVENT 742END:VCALENDAR 743ICS; 744 745 $this->assertValidate( 746 $input, 747 VCalendar::PROFILE_CALDAV, 748 3, 749 "A calendar object on a CalDAV server MUST NOT have a METHOD property." 750 ); 751 752 } 753 754 function assertValidate($ics, $options, $expectedLevel, $expectedMessage = null) { 755 756 $vcal = VObject\Reader::read($ics); 757 $result = $vcal->validate($options); 758 759 $this->assertValidateResult($result, $expectedLevel, $expectedMessage); 760 761 } 762 763 function assertValidateResult($input, $expectedLevel, $expectedMessage = null) { 764 765 $messages = []; 766 foreach ($input as $warning) { 767 $messages[] = $warning['message']; 768 } 769 770 if ($expectedLevel === 0) { 771 $this->assertEquals(0, count($input), 'No validation messages were expected. We got: ' . implode(', ', $messages)); 772 } else { 773 $this->assertEquals(1, count($input), 'We expected exactly 1 validation message, We got: ' . implode(', ', $messages)); 774 775 $this->assertEquals($expectedMessage, $input[0]['message']); 776 $this->assertEquals($expectedLevel, $input[0]['level']); 777 } 778 779 } 780 781 782} 783