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 260END:VEVENT 261BEGIN:VEVENT 262UID:bla3 263DTSTART;VALUE=DATE:20141113 264RECURRENCE-ID;VALUE=DATE:20141113 265END:VEVENT 266END:VCALENDAR 267ICS; 268 269 $tests[] = array($input, $output, 'America/Argentina/Buenos_Aires', '2014-01-01', '2015-01-01'); 270 271 // Recurrence rule with no valid instances 272 $input = 'BEGIN:VCALENDAR 273CALSCALE:GREGORIAN 274VERSION:2.0 275BEGIN:VEVENT 276UID:bla6 277SUMMARY:Testing RRule3 278DTSTART:20111125T120000Z 279DTEND:20111125T130000Z 280RRULE:FREQ=WEEKLY;COUNT=1 281EXDATE:20111125T120000Z 282END:VEVENT 283END:VCALENDAR 284'; 285 286 $output = 'BEGIN:VCALENDAR 287CALSCALE:GREGORIAN 288VERSION:2.0 289END:VCALENDAR 290'; 291 292 $tests[] = array($input, $output); 293 return $tests; 294 295 } 296 297 /** 298 * @expectedException LogicException 299 */ 300 public function testBrokenEventExpand() { 301 302 $input = 'BEGIN:VCALENDAR 303CALSCALE:GREGORIAN 304VERSION:2.0 305BEGIN:VEVENT 306RRULE:FREQ=WEEKLY 307DTSTART;VALUE=DATE:20111202 308END:VEVENT 309END:VCALENDAR 310'; 311 $vcal = VObject\Reader::read($input); 312 $vcal->expand( 313 new \DateTime('2011-12-01'), 314 new \DateTime('2011-12-31') 315 ); 316 317 } 318 319 function testGetDocumentType() { 320 321 $vcard = new VCalendar(); 322 $vcard->VERSION = '2.0'; 323 $this->assertEquals(VCalendar::ICALENDAR20, $vcard->getDocumentType()); 324 325 } 326 327 function testValidateCorrect() { 328 329 $input = 'BEGIN:VCALENDAR 330CALSCALE:GREGORIAN 331VERSION:2.0 332PRODID:foo 333BEGIN:VEVENT 334DTSTART;VALUE=DATE:20111202 335DTSTAMP:20140122T233226Z 336UID:foo 337END:VEVENT 338END:VCALENDAR 339'; 340 341 $vcal = VObject\Reader::read($input); 342 $this->assertEquals(array(), $vcal->validate(), 'Got an error'); 343 344 } 345 346 function testValidateNoVersion() { 347 348 $input = 'BEGIN:VCALENDAR 349CALSCALE:GREGORIAN 350PRODID:foo 351BEGIN:VEVENT 352DTSTART;VALUE=DATE:20111202 353UID:foo 354DTSTAMP:20140122T234434Z 355END:VEVENT 356END:VCALENDAR 357'; 358 359 $vcal = VObject\Reader::read($input); 360 $this->assertEquals(1, count($vcal->validate())); 361 362 } 363 364 function testValidateWrongVersion() { 365 366 $input = 'BEGIN:VCALENDAR 367CALSCALE:GREGORIAN 368VERSION:3.0 369PRODID:foo 370BEGIN:VEVENT 371DTSTART;VALUE=DATE:20111202 372UID:foo 373DTSTAMP:20140122T234434Z 374END:VEVENT 375END:VCALENDAR 376'; 377 378 $vcal = VObject\Reader::read($input); 379 $this->assertEquals(1, count($vcal->validate())); 380 381 } 382 383 function testValidateNoProdId() { 384 385 $input = 'BEGIN:VCALENDAR 386CALSCALE:GREGORIAN 387VERSION:2.0 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 testValidateDoubleCalScale() { 402 403 $input = 'BEGIN:VCALENDAR 404VERSION:2.0 405PRODID:foo 406CALSCALE:GREGORIAN 407CALSCALE:GREGORIAN 408BEGIN:VEVENT 409DTSTART;VALUE=DATE:20111202 410UID:foo 411DTSTAMP:20140122T234434Z 412END:VEVENT 413END:VCALENDAR 414'; 415 416 $vcal = VObject\Reader::read($input); 417 $this->assertEquals(1, count($vcal->validate())); 418 419 } 420 421 function testValidateDoubleMethod() { 422 423 $input = 'BEGIN:VCALENDAR 424VERSION:2.0 425PRODID:foo 426METHOD:REQUEST 427METHOD:REQUEST 428BEGIN:VEVENT 429DTSTART;VALUE=DATE:20111202 430UID:foo 431DTSTAMP:20140122T234434Z 432END:VEVENT 433END:VCALENDAR 434'; 435 436 $vcal = VObject\Reader::read($input); 437 $this->assertEquals(1, count($vcal->validate())); 438 439 } 440 441 function testValidateTwoMasterEvents() { 442 443 $input = 'BEGIN:VCALENDAR 444VERSION:2.0 445PRODID:foo 446METHOD:REQUEST 447BEGIN:VEVENT 448DTSTART;VALUE=DATE:20111202 449UID:foo 450DTSTAMP:20140122T234434Z 451END:VEVENT 452BEGIN:VEVENT 453DTSTART;VALUE=DATE:20111202 454UID:foo 455DTSTAMP:20140122T234434Z 456END:VEVENT 457END:VCALENDAR 458'; 459 460 $vcal = VObject\Reader::read($input); 461 $this->assertEquals(1, count($vcal->validate())); 462 463 } 464 465 function testValidateOneMasterEvent() { 466 467 $input = 'BEGIN:VCALENDAR 468VERSION:2.0 469PRODID:foo 470METHOD:REQUEST 471BEGIN:VEVENT 472DTSTART;VALUE=DATE:20111202 473UID:foo 474DTSTAMP:20140122T234434Z 475END:VEVENT 476BEGIN:VEVENT 477DTSTART;VALUE=DATE:20111202 478UID:foo 479DTSTAMP:20140122T234434Z 480RECURRENCE-ID;VALUE=DATE:20111202 481END:VEVENT 482END:VCALENDAR 483'; 484 485 $vcal = VObject\Reader::read($input); 486 $this->assertEquals(0, count($vcal->validate())); 487 488 } 489 490 function testGetBaseComponent() { 491 492 $input = 'BEGIN:VCALENDAR 493VERSION:2.0 494PRODID:foo 495METHOD:REQUEST 496BEGIN:VEVENT 497SUMMARY:test 498DTSTART;VALUE=DATE:20111202 499UID:foo 500DTSTAMP:20140122T234434Z 501END:VEVENT 502BEGIN:VEVENT 503DTSTART;VALUE=DATE:20111202 504UID:foo 505DTSTAMP:20140122T234434Z 506RECURRENCE-ID;VALUE=DATE:20111202 507END:VEVENT 508END:VCALENDAR 509'; 510 511 $vcal = VObject\Reader::read($input); 512 513 $result = $vcal->getBaseComponent(); 514 $this->assertEquals('test', $result->SUMMARY->getValue()); 515 516 } 517 518 function testGetBaseComponentNoResult() { 519 520 $input = 'BEGIN:VCALENDAR 521VERSION:2.0 522PRODID:foo 523METHOD:REQUEST 524BEGIN:VEVENT 525SUMMARY:test 526RECURRENCE-ID;VALUE=DATE:20111202 527DTSTART;VALUE=DATE:20111202 528UID:foo 529DTSTAMP:20140122T234434Z 530END:VEVENT 531BEGIN:VEVENT 532DTSTART;VALUE=DATE:20111202 533UID:foo 534DTSTAMP:20140122T234434Z 535RECURRENCE-ID;VALUE=DATE:20111202 536END:VEVENT 537END:VCALENDAR 538'; 539 540 $vcal = VObject\Reader::read($input); 541 542 $result = $vcal->getBaseComponent(); 543 $this->assertNull($result); 544 545 } 546 547 function testNoComponents() { 548 549 $input = <<<ICS 550BEGIN:VCALENDAR 551VERSION:2.0 552PRODID:vobject 553END:VCALENDAR 554ICS; 555 556 $this->assertValidate( 557 $input, 558 0, 559 3, 560 "An iCalendar object must have at least 1 component." 561 ); 562 563 } 564 565 function testCalDAVNoComponents() { 566 567 $input = <<<ICS 568BEGIN:VCALENDAR 569VERSION:2.0 570PRODID:vobject 571BEGIN:VTIMEZONE 572TZID:America/Toronto 573END:VTIMEZONE 574END:VCALENDAR 575ICS; 576 577 $this->assertValidate( 578 $input, 579 VCalendar::PROFILE_CALDAV, 580 3, 581 "A calendar object on a CalDAV server must have at least 1 component (VTODO, VEVENT, VJOURNAL)." 582 ); 583 584 } 585 586 function testCalDAVMultiUID() { 587 588 $input = <<<ICS 589BEGIN:VCALENDAR 590VERSION:2.0 591PRODID:vobject 592BEGIN:VEVENT 593UID:foo 594DTSTAMP:20150109T184500Z 595DTSTART:20150109T184500Z 596END:VEVENT 597BEGIN:VEVENT 598UID:bar 599DTSTAMP:20150109T184500Z 600DTSTART:20150109T184500Z 601END:VEVENT 602END:VCALENDAR 603ICS; 604 605 $this->assertValidate( 606 $input, 607 VCalendar::PROFILE_CALDAV, 608 3, 609 "A calendar object on a CalDAV server may only have components with the same UID." 610 ); 611 612 } 613 614 function testCalDAVMultiComponent() { 615 616 $input = <<<ICS 617BEGIN:VCALENDAR 618VERSION:2.0 619PRODID:vobject 620BEGIN:VEVENT 621UID:foo 622RECURRENCE-ID:20150109T185200Z 623DTSTAMP:20150109T184500Z 624DTSTART:20150109T184500Z 625END:VEVENT 626BEGIN:VTODO 627UID:foo 628DTSTAMP:20150109T184500Z 629DTSTART:20150109T184500Z 630END:VTODO 631END:VCALENDAR 632ICS; 633 634 $this->assertValidate( 635 $input, 636 VCalendar::PROFILE_CALDAV, 637 3, 638 "A calendar object on a CalDAV server may only have 1 type of component (VEVENT, VTODO or VJOURNAL)." 639 ); 640 641 } 642 643 function testCalDAVMETHOD() { 644 645 $input = <<<ICS 646BEGIN:VCALENDAR 647VERSION:2.0 648METHOD:PUBLISH 649PRODID:vobject 650BEGIN:VEVENT 651UID:foo 652RECURRENCE-ID:20150109T185200Z 653DTSTAMP:20150109T184500Z 654DTSTART:20150109T184500Z 655END:VEVENT 656END:VCALENDAR 657ICS; 658 659 $this->assertValidate( 660 $input, 661 VCalendar::PROFILE_CALDAV, 662 3, 663 "A calendar object on a CalDAV server MUST NOT have a METHOD property." 664 ); 665 666 } 667 668 function assertValidate($ics, $options, $expectedLevel, $expectedMessage = null) { 669 670 $vcal = VObject\Reader::read($ics); 671 $result = $vcal->validate($options); 672 673 $this->assertValidateResult($result, $expectedLevel, $expectedMessage); 674 675 } 676 677 function assertValidateResult($input, $expectedLevel, $expectedMessage = null) { 678 679 $messages = array(); 680 foreach($input as $warning) { 681 $messages[] = $warning['message']; 682 } 683 684 if ($expectedLevel === 0) { 685 $this->assertEquals(0, count($input), 'No validation messages were expected. We got: ' . implode(', ', $messages)); 686 } else { 687 $this->assertEquals(1, count($input), 'We expected exactly 1 validation message, We got: ' . implode(', ', $messages)); 688 689 $this->assertEquals($expectedMessage, $input[0]['message']); 690 $this->assertEquals($expectedLevel, $input[0]['level']); 691 } 692 693 } 694 695 696} 697