1<?php 2 3namespace Sabre\VObject\Component; 4 5use DateTimeImmutable; 6use DateTimeZone; 7use PHPUnit\Framework\TestCase; 8use Sabre\VObject; 9use Sabre\VObject\Reader; 10 11/** 12 * We use `RFCxxx` has a placeholder for the 13 * https://tools.ietf.org/html/draft-daboo-calendar-availability-05 name. 14 */ 15class VAvailabilityTest extends TestCase { 16 17 function testVAvailabilityComponent() { 18 19 $vcal = <<<VCAL 20BEGIN:VCALENDAR 21BEGIN:VAVAILABILITY 22END:VAVAILABILITY 23END:VCALENDAR 24VCAL; 25 $document = Reader::read($vcal); 26 27 $this->assertInstanceOf(__NAMESPACE__ . '\VAvailability', $document->VAVAILABILITY); 28 29 } 30 31 function testGetEffectiveStartEnd() { 32 33 $vcal = <<<VCAL 34BEGIN:VCALENDAR 35BEGIN:VAVAILABILITY 36DTSTART:20150717T162200Z 37DTEND:20150717T172200Z 38END:VAVAILABILITY 39END:VCALENDAR 40VCAL; 41 42 $document = Reader::read($vcal); 43 $tz = new DateTimeZone('UTC'); 44 $this->assertEquals( 45 [ 46 new DateTimeImmutable('2015-07-17 16:22:00', $tz), 47 new DateTimeImmutable('2015-07-17 17:22:00', $tz), 48 ], 49 $document->VAVAILABILITY->getEffectiveStartEnd() 50 ); 51 52 } 53 54 function testGetEffectiveStartDuration() { 55 56 $vcal = <<<VCAL 57BEGIN:VCALENDAR 58BEGIN:VAVAILABILITY 59DTSTART:20150717T162200Z 60DURATION:PT1H 61END:VAVAILABILITY 62END:VCALENDAR 63VCAL; 64 65 $document = Reader::read($vcal); 66 $tz = new DateTimeZone('UTC'); 67 $this->assertEquals( 68 [ 69 new DateTimeImmutable('2015-07-17 16:22:00', $tz), 70 new DateTimeImmutable('2015-07-17 17:22:00', $tz), 71 ], 72 $document->VAVAILABILITY->getEffectiveStartEnd() 73 ); 74 75 } 76 77 function testGetEffectiveStartEndUnbound() { 78 79 $vcal = <<<VCAL 80BEGIN:VCALENDAR 81BEGIN:VAVAILABILITY 82END:VAVAILABILITY 83END:VCALENDAR 84VCAL; 85 86 $document = Reader::read($vcal); 87 $this->assertEquals( 88 [ 89 null, 90 null, 91 ], 92 $document->VAVAILABILITY->getEffectiveStartEnd() 93 ); 94 95 } 96 97 function testIsInTimeRangeUnbound() { 98 99 $vcal = <<<VCAL 100BEGIN:VCALENDAR 101BEGIN:VAVAILABILITY 102END:VAVAILABILITY 103END:VCALENDAR 104VCAL; 105 106 $document = Reader::read($vcal); 107 $this->assertTrue( 108 $document->VAVAILABILITY->isInTimeRange(new DateTimeImmutable('2015-07-17'), new DateTimeImmutable('2015-07-18')) 109 ); 110 111 } 112 113 function testIsInTimeRangeOutside() { 114 115 $vcal = <<<VCAL 116BEGIN:VCALENDAR 117BEGIN:VAVAILABILITY 118DTSTART:20140101T000000Z 119DTEND:20140102T000000Z 120END:VAVAILABILITY 121END:VCALENDAR 122VCAL; 123 124 $document = Reader::read($vcal); 125 $this->assertFalse( 126 $document->VAVAILABILITY->isInTimeRange(new DateTimeImmutable('2015-07-17'), new DateTimeImmutable('2015-07-18')) 127 ); 128 129 } 130 131 function testRFCxxxSection3_1_availabilityprop_required() { 132 133 // UID and DTSTAMP are present. 134 $this->assertIsValid(Reader::read( 135<<<VCAL 136BEGIN:VCALENDAR 137VERSION:2.0 138PRODID:-//id 139BEGIN:VAVAILABILITY 140UID:foo@test 141DTSTAMP:20111005T133225Z 142END:VAVAILABILITY 143END:VCALENDAR 144VCAL 145 )); 146 147 // UID and DTSTAMP are missing. 148 $this->assertIsNotValid(Reader::read( 149<<<VCAL 150BEGIN:VCALENDAR 151VERSION:2.0 152PRODID:-//id 153BEGIN:VAVAILABILITY 154END:VAVAILABILITY 155END:VCALENDAR 156VCAL 157 )); 158 159 // DTSTAMP is missing. 160 $this->assertIsNotValid(Reader::read( 161<<<VCAL 162BEGIN:VCALENDAR 163VERSION:2.0 164PRODID:-//id 165BEGIN:VAVAILABILITY 166UID:foo@test 167END:VAVAILABILITY 168END:VCALENDAR 169VCAL 170 )); 171 172 // UID is missing. 173 $this->assertIsNotValid(Reader::read( 174<<<VCAL 175BEGIN:VCALENDAR 176VERSION:2.0 177PRODID:-//id 178BEGIN:VAVAILABILITY 179DTSTAMP:20111005T133225Z 180END:VAVAILABILITY 181END:VCALENDAR 182VCAL 183 )); 184 185 } 186 187 function testRFCxxxSection3_1_availabilityprop_optional_once() { 188 189 $properties = [ 190 'BUSYTYPE:BUSY', 191 'CLASS:PUBLIC', 192 'CREATED:20111005T135125Z', 193 'DESCRIPTION:Long bla bla', 194 'DTSTART:20111005T020000', 195 'LAST-MODIFIED:20111005T135325Z', 196 'ORGANIZER:mailto:foo@example.com', 197 'PRIORITY:1', 198 'SEQUENCE:0', 199 'SUMMARY:Bla bla', 200 'URL:http://example.org/' 201 ]; 202 203 // They are all present, only once. 204 $this->assertIsValid(Reader::read($this->template($properties))); 205 206 // We duplicate each one to see if it fails. 207 foreach ($properties as $property) { 208 $this->assertIsNotValid(Reader::read($this->template([ 209 $property, 210 $property 211 ]))); 212 } 213 214 } 215 216 function testRFCxxxSection3_1_availabilityprop_dtend_duration() { 217 218 // Only DTEND. 219 $this->assertIsValid(Reader::read($this->template([ 220 'DTEND:21111005T133225Z' 221 ]))); 222 223 // Only DURATION. 224 $this->assertIsValid(Reader::read($this->template([ 225 'DURATION:PT1H' 226 ]))); 227 228 // Both (not allowed). 229 $this->assertIsNotValid(Reader::read($this->template([ 230 'DTEND:21111005T133225Z', 231 'DURATION:PT1H' 232 ]))); 233 } 234 235 function testAvailableSubComponent() { 236 237 $vcal = <<<VCAL 238BEGIN:VCALENDAR 239BEGIN:VAVAILABILITY 240BEGIN:AVAILABLE 241END:AVAILABLE 242END:VAVAILABILITY 243END:VCALENDAR 244VCAL; 245 $document = Reader::read($vcal); 246 247 $this->assertInstanceOf(__NAMESPACE__, $document->VAVAILABILITY->AVAILABLE); 248 249 } 250 251 function testRFCxxxSection3_1_availableprop_required() { 252 253 // UID, DTSTAMP and DTSTART are present. 254 $this->assertIsValid(Reader::read( 255<<<VCAL 256BEGIN:VCALENDAR 257VERSION:2.0 258PRODID:-//id 259BEGIN:VAVAILABILITY 260UID:foo@test 261DTSTAMP:20111005T133225Z 262BEGIN:AVAILABLE 263UID:foo@test 264DTSTAMP:20111005T133225Z 265DTSTART:20111005T133225Z 266END:AVAILABLE 267END:VAVAILABILITY 268END:VCALENDAR 269VCAL 270 )); 271 272 // UID, DTSTAMP and DTSTART are missing. 273 $this->assertIsNotValid(Reader::read( 274<<<VCAL 275BEGIN:VCALENDAR 276VERSION:2.0 277PRODID:-//id 278BEGIN:VAVAILABILITY 279UID:foo@test 280DTSTAMP:20111005T133225Z 281BEGIN:AVAILABLE 282END:AVAILABLE 283END:VAVAILABILITY 284END:VCALENDAR 285VCAL 286 )); 287 288 // UID is missing. 289 $this->assertIsNotValid(Reader::read( 290<<<VCAL 291BEGIN:VCALENDAR 292VERSION:2.0 293PRODID:-//id 294BEGIN:VAVAILABILITY 295UID:foo@test 296DTSTAMP:20111005T133225Z 297BEGIN:AVAILABLE 298DTSTAMP:20111005T133225Z 299DTSTART:20111005T133225Z 300END:AVAILABLE 301END:VAVAILABILITY 302END:VCALENDAR 303VCAL 304 )); 305 306 // DTSTAMP is missing. 307 $this->assertIsNotValid(Reader::read( 308<<<VCAL 309BEGIN:VCALENDAR 310VERSION:2.0 311PRODID:-//id 312BEGIN:VAVAILABILITY 313UID:foo@test 314DTSTAMP:20111005T133225Z 315BEGIN:AVAILABLE 316UID:foo@test 317DTSTART:20111005T133225Z 318END:AVAILABLE 319END:VAVAILABILITY 320END:VCALENDAR 321VCAL 322 )); 323 324 // DTSTART is missing. 325 $this->assertIsNotValid(Reader::read( 326<<<VCAL 327BEGIN:VCALENDAR 328VERSION:2.0 329PRODID:-//id 330BEGIN:VAVAILABILITY 331UID:foo@test 332DTSTAMP:20111005T133225Z 333BEGIN:AVAILABLE 334UID:foo@test 335DTSTAMP:20111005T133225Z 336END:AVAILABLE 337END:VAVAILABILITY 338END:VCALENDAR 339VCAL 340 )); 341 342 } 343 344 function testRFCxxxSection3_1_available_dtend_duration() { 345 346 // Only DTEND. 347 $this->assertIsValid(Reader::read($this->templateAvailable([ 348 'DTEND:21111005T133225Z' 349 ]))); 350 351 // Only DURATION. 352 $this->assertIsValid(Reader::read($this->templateAvailable([ 353 'DURATION:PT1H' 354 ]))); 355 356 // Both (not allowed). 357 $this->assertIsNotValid(Reader::read($this->templateAvailable([ 358 'DTEND:21111005T133225Z', 359 'DURATION:PT1H' 360 ]))); 361 } 362 363 function testRFCxxxSection3_1_available_optional_once() { 364 365 $properties = [ 366 'CREATED:20111005T135125Z', 367 'DESCRIPTION:Long bla bla', 368 'LAST-MODIFIED:20111005T135325Z', 369 'RECURRENCE-ID;RANGE=THISANDFUTURE:19980401T133000Z', 370 'RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR', 371 'SUMMARY:Bla bla' 372 ]; 373 374 // They are all present, only once. 375 $this->assertIsValid(Reader::read($this->templateAvailable($properties))); 376 377 // We duplicate each one to see if it fails. 378 foreach ($properties as $property) { 379 $this->assertIsNotValid(Reader::read($this->templateAvailable([ 380 $property, 381 $property 382 ]))); 383 } 384 385 } 386 function testRFCxxxSection3_2() { 387 388 $this->assertEquals( 389 'BUSY', 390 Reader::read($this->templateAvailable([ 391 'BUSYTYPE:BUSY' 392 ])) 393 ->VAVAILABILITY 394 ->AVAILABLE 395 ->BUSYTYPE 396 ->getValue() 397 ); 398 399 $this->assertEquals( 400 'BUSY-UNAVAILABLE', 401 Reader::read($this->templateAvailable([ 402 'BUSYTYPE:BUSY-UNAVAILABLE' 403 ])) 404 ->VAVAILABILITY 405 ->AVAILABLE 406 ->BUSYTYPE 407 ->getValue() 408 ); 409 410 $this->assertEquals( 411 'BUSY-TENTATIVE', 412 Reader::read($this->templateAvailable([ 413 'BUSYTYPE:BUSY-TENTATIVE' 414 ])) 415 ->VAVAILABILITY 416 ->AVAILABLE 417 ->BUSYTYPE 418 ->getValue() 419 ); 420 421 } 422 423 protected function assertIsValid(VObject\Document $document) { 424 425 $validationResult = $document->validate(); 426 if ($validationResult) { 427 $messages = array_map(function($item) { return $item['message']; }, $validationResult); 428 $this->fail('Failed to assert that the supplied document is a valid document. Validation messages: ' . implode(', ', $messages)); 429 } 430 $this->assertEmpty($document->validate()); 431 432 } 433 434 protected function assertIsNotValid(VObject\Document $document) { 435 436 $this->assertNotEmpty($document->validate()); 437 438 } 439 440 protected function template(array $properties) { 441 442 return $this->_template( 443 <<<VCAL 444BEGIN:VCALENDAR 445VERSION:2.0 446PRODID:-//id 447BEGIN:VAVAILABILITY 448UID:foo@test 449DTSTAMP:20111005T133225Z 450… 451END:VAVAILABILITY 452END:VCALENDAR 453VCAL 454, 455 $properties 456 ); 457 458 } 459 460 protected function templateAvailable(array $properties) { 461 462 return $this->_template( 463 <<<VCAL 464BEGIN:VCALENDAR 465VERSION:2.0 466PRODID:-//id 467BEGIN:VAVAILABILITY 468UID:foo@test 469DTSTAMP:20111005T133225Z 470BEGIN:AVAILABLE 471UID:foo@test 472DTSTAMP:20111005T133225Z 473DTSTART:20111005T133225Z 474… 475END:AVAILABLE 476END:VAVAILABILITY 477END:VCALENDAR 478VCAL 479, 480 $properties 481 ); 482 483 } 484 485 protected function _template($template, array $properties) { 486 487 return str_replace('…', implode("\r\n", $properties), $template); 488 489 } 490 491} 492