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