1<?php
2
3namespace Sabre\VObject;
4
5class FreeBusyGeneratorTest extends \PHPUnit_Framework_TestCase {
6
7    use PHPUnitAssertions;
8
9    function testGeneratorBaseObject() {
10
11        $obj = new Component\VCalendar();
12        $obj->METHOD = 'PUBLISH';
13
14        $gen = new FreeBusyGenerator();
15        $gen->setObjects([]);
16        $gen->setBaseObject($obj);
17
18        $result = $gen->getResult();
19
20        $this->assertEquals('PUBLISH', $result->METHOD->getValue());
21
22    }
23
24    /**
25     * @expectedException InvalidArgumentException
26     */
27    function testInvalidArg() {
28
29        $gen = new FreeBusyGenerator(
30            new \DateTime('2012-01-01'),
31            new \DateTime('2012-12-31'),
32            new \StdClass()
33        );
34
35    }
36
37    /**
38     * This function takes a list of objects (icalendar objects), and turns
39     * them into a freebusy report.
40     *
41     * Then it takes the expected output and compares it to what we actually
42     * got.
43     *
44     * It only generates the freebusy report for the following time-range:
45     * 2011-01-01 11:00:00 until 2011-01-03 11:11:11
46     *
47     * @param string $expected
48     * @param array $input
49     * @param string|null $timeZone
50     * @param string $vavailability
51     * @return void
52     */
53    function assertFreeBusyReport($expected, $input, $timeZone = null, $vavailability = null) {
54
55        $gen = new FreeBusyGenerator(
56            new \DateTime('20110101T110000Z', new \DateTimeZone('UTC')),
57            new \DateTime('20110103T110000Z', new \DateTimeZone('UTC')),
58            $input,
59            $timeZone
60        );
61
62        if ($vavailability) {
63            if (is_string($vavailability)) {
64                $vavailability = Reader::read($vavailability);
65            }
66            $gen->setVAvailability($vavailability);
67        }
68
69        $output = $gen->getResult();
70
71        // Removing DTSTAMP because it changes every time.
72        unset($output->VFREEBUSY->DTSTAMP);
73
74        $expected = <<<ICS
75BEGIN:VCALENDAR
76VERSION:2.0
77BEGIN:VFREEBUSY
78DTSTART:20110101T110000Z
79DTEND:20110103T110000Z
80$expected
81END:VFREEBUSY
82END:VCALENDAR
83ICS;
84
85        $this->assertVObjectEqualsVObject($expected, $output);
86
87    }
88
89    function testSimple() {
90
91        $blob = <<<ICS
92BEGIN:VCALENDAR
93BEGIN:VEVENT
94UID:foobar
95DTSTART:20110101T120000Z
96DTEND:20110101T130000Z
97END:VEVENT
98END:VCALENDAR
99ICS;
100
101
102        $this->assertFreeBusyReport(
103            "FREEBUSY:20110101T120000Z/20110101T130000Z",
104            $blob
105        );
106
107    }
108
109    function testSource() {
110
111        $blob = <<<ICS
112BEGIN:VCALENDAR
113BEGIN:VEVENT
114UID:foobar
115DTSTART:20110101T120000Z
116DTEND:20110101T130000Z
117END:VEVENT
118END:VCALENDAR
119ICS;
120        $h = fopen('php://memory', 'r+');
121        fwrite($h, $blob);
122        rewind($h);
123
124
125        $this->assertFreeBusyReport(
126            "FREEBUSY:20110101T120000Z/20110101T130000Z",
127            $h
128        );
129
130    }
131
132    /**
133     * Testing TRANSP:OPAQUE
134     */
135    function testOpaque() {
136
137        $blob = <<<ICS
138BEGIN:VCALENDAR
139BEGIN:VEVENT
140UID:foobar2
141TRANSP:OPAQUE
142DTSTART:20110101T130000Z
143DTEND:20110101T140000Z
144END:VEVENT
145END:VCALENDAR
146ICS;
147
148        $this->assertFreeBusyReport(
149            "FREEBUSY:20110101T130000Z/20110101T140000Z",
150            $blob
151        );
152
153    }
154
155    /**
156     * Testing TRANSP:TRANSPARENT
157     */
158    function testTransparent() {
159
160        // transparent, hidden
161        $blob = <<<ICS
162BEGIN:VCALENDAR
163BEGIN:VEVENT
164UID:foobar3
165TRANSP:TRANSPARENT
166DTSTART:20110101T140000Z
167DTEND:20110101T150000Z
168END:VEVENT
169END:VCALENDAR
170ICS;
171
172        $this->assertFreeBusyReport(
173            "",
174            $blob
175        );
176
177    }
178
179    /**
180     * Testing STATUS:CANCELLED
181     */
182    function testCancelled() {
183
184        // transparent, hidden
185        $blob = <<<ICS
186BEGIN:VCALENDAR
187BEGIN:VEVENT
188UID:foobar4
189STATUS:CANCELLED
190DTSTART:20110101T160000Z
191DTEND:20110101T170000Z
192END:VEVENT
193END:VCALENDAR
194ICS;
195
196        $this->assertFreeBusyReport(
197            "",
198            $blob
199        );
200
201    }
202
203    /**
204     * Testing STATUS:TENTATIVE
205     */
206    function testTentative() {
207
208        // tentative, shows up
209        $blob = <<<ICS
210BEGIN:VCALENDAR
211BEGIN:VEVENT
212UID:foobar5
213STATUS:TENTATIVE
214DTSTART:20110101T180000Z
215DTEND:20110101T190000Z
216END:VEVENT
217END:VCALENDAR
218ICS;
219
220        $this->assertFreeBusyReport(
221            'FREEBUSY;FBTYPE=BUSY-TENTATIVE:20110101T180000Z/20110101T190000Z',
222            $blob
223        );
224
225    }
226
227    /**
228     * Testing an event that falls outside of the report time-range.
229     */
230    function testOutsideTimeRange() {
231
232        // outside of time-range, hidden
233        $blob = <<<ICS
234BEGIN:VCALENDAR
235BEGIN:VEVENT
236UID:foobar6
237DTSTART:20110101T090000Z
238DTEND:20110101T100000Z
239END:VEVENT
240END:VCALENDAR
241ICS;
242
243        $this->assertFreeBusyReport(
244            '',
245            $blob
246        );
247
248    }
249
250    /**
251     * Testing an event that falls outside of the report time-range.
252     */
253    function testOutsideTimeRange2() {
254
255        // outside of time-range, hidden
256        $blob = <<<ICS
257BEGIN:VCALENDAR
258BEGIN:VEVENT
259UID:foobar7
260DTSTART:20110104T090000Z
261DTEND:20110104T100000Z
262END:VEVENT
263END:VCALENDAR
264ICS;
265
266        $this->assertFreeBusyReport(
267            '',
268            $blob
269        );
270
271    }
272
273    /**
274     * Testing an event that uses DURATION
275     */
276    function testDuration() {
277
278        // using duration, shows up
279        $blob = <<<ICS
280BEGIN:VCALENDAR
281BEGIN:VEVENT
282UID:foobar8
283DTSTART:20110101T190000Z
284DURATION:PT1H
285END:VEVENT
286END:VCALENDAR
287ICS;
288
289        $this->assertFreeBusyReport(
290            'FREEBUSY:20110101T190000Z/20110101T200000Z',
291            $blob
292        );
293
294    }
295
296    /**
297     * Testing an all-day event
298     */
299    function testAllDay() {
300
301        // Day-long event, shows up
302        $blob = <<<ICS
303BEGIN:VCALENDAR
304BEGIN:VEVENT
305UID:foobar9
306DTSTART;VALUE=DATE:20110102
307END:VEVENT
308END:VCALENDAR
309ICS;
310
311        $this->assertFreeBusyReport(
312            'FREEBUSY:20110102T000000Z/20110103T000000Z',
313            $blob
314        );
315
316    }
317
318    /**
319     * Testing an event that has no end or duration.
320     */
321    function testNoDuration() {
322
323        // No duration, does not show up
324        $blob = <<<ICS
325BEGIN:VCALENDAR
326BEGIN:VEVENT
327UID:foobar10
328DTSTART:20110101T200000Z
329END:VEVENT
330END:VCALENDAR
331ICS;
332
333        $this->assertFreeBusyReport(
334            '',
335            $blob
336        );
337
338    }
339
340    /**
341     * Testing feeding the freebusy generator an object instead of a string.
342     */
343    function testObject() {
344
345        // encoded as object, shows up
346        $blob = <<<ICS
347BEGIN:VCALENDAR
348BEGIN:VEVENT
349UID:foobar11
350DTSTART:20110101T210000Z
351DURATION:PT1H
352END:VEVENT
353END:VCALENDAR
354ICS;
355
356        $this->assertFreeBusyReport(
357            'FREEBUSY:20110101T210000Z/20110101T220000Z',
358            Reader::read($blob)
359        );
360
361
362    }
363
364    /**
365     * Testing feeding VFREEBUSY objects instead of VEVENT
366     */
367    function testVFreeBusy() {
368
369        // Freebusy. Some parts show up
370        $blob = <<<ICS
371BEGIN:VCALENDAR
372BEGIN:VFREEBUSY
373FREEBUSY:20110103T010000Z/20110103T020000Z
374FREEBUSY;FBTYPE=FREE:20110103T020000Z/20110103T030000Z
375FREEBUSY:20110103T030000Z/20110103T040000Z,20110103T040000Z/20110103T050000Z
376FREEBUSY:20120101T000000Z/20120101T010000Z
377FREEBUSY:20110103T050000Z/PT1H
378END:VFREEBUSY
379END:VCALENDAR
380ICS;
381
382        $this->assertFreeBusyReport(
383            "FREEBUSY:20110103T010000Z/20110103T020000Z\n" .
384            'FREEBUSY:20110103T030000Z/20110103T060000Z',
385            $blob
386        );
387
388    }
389
390    function testYearlyRecurrence() {
391
392        // Yearly recurrence rule, shows up
393        $blob = <<<ICS
394BEGIN:VCALENDAR
395BEGIN:VEVENT
396UID:foobar13
397DTSTART:20100101T220000Z
398DTEND:20100101T230000Z
399RRULE:FREQ=YEARLY
400END:VEVENT
401END:VCALENDAR
402ICS;
403
404        $this->assertFreeBusyReport(
405            'FREEBUSY:20110101T220000Z/20110101T230000Z',
406            $blob
407        );
408
409    }
410
411    function testYearlyRecurrenceDuration() {
412
413        // Yearly recurrence rule + duration, shows up
414        $blob = <<<ICS
415BEGIN:VCALENDAR
416BEGIN:VEVENT
417UID:foobar14
418DTSTART:20100101T230000Z
419DURATION:PT1H
420RRULE:FREQ=YEARLY
421END:VEVENT
422END:VCALENDAR
423ICS;
424
425        $this->assertFreeBusyReport(
426            'FREEBUSY:20110101T230000Z/20110102T000000Z',
427            $blob
428        );
429
430    }
431
432    function testFloatingTime() {
433
434        // Floating time, no timezone
435        $blob = <<<ICS
436BEGIN:VCALENDAR
437BEGIN:VEVENT
438UID:foobar
439DTSTART:20110101T120000
440DTEND:20110101T130000
441END:VEVENT
442END:VCALENDAR
443ICS;
444
445        $this->assertFreeBusyReport(
446            "FREEBUSY:20110101T120000Z/20110101T130000Z",
447            $blob
448        );
449
450    }
451
452    function testFloatingTimeReferenceTimeZone() {
453
454        // Floating time + reference timezone
455        $blob = <<<ICS
456BEGIN:VCALENDAR
457BEGIN:VEVENT
458UID:foobar
459DTSTART:20110101T120000
460DTEND:20110101T130000
461END:VEVENT
462END:VCALENDAR
463ICS;
464
465        $this->assertFreeBusyReport(
466            "FREEBUSY:20110101T170000Z/20110101T180000Z",
467            $blob,
468            new \DateTimeZone('America/Toronto')
469        );
470
471    }
472
473    function testAllDay2() {
474
475        // All-day event, slightly outside of the VFREEBUSY range.
476        $blob = <<<ICS
477BEGIN:VCALENDAR
478BEGIN:VEVENT
479UID:foobar
480DTSTART;VALUE=DATE:20110101
481END:VEVENT
482END:VCALENDAR
483ICS;
484
485        $this->assertFreeBusyReport(
486            "FREEBUSY:20110101T110000Z/20110102T000000Z",
487            $blob
488        );
489
490    }
491
492    function testAllDayReferenceTimeZone() {
493
494        // All-day event + reference timezone
495        $blob = <<<ICS
496BEGIN:VCALENDAR
497BEGIN:VEVENT
498UID:foobar
499DTSTART;VALUE=DATE:20110101
500END:VEVENT
501END:VCALENDAR
502ICS;
503
504        $this->assertFreeBusyReport(
505            "FREEBUSY:20110101T110000Z/20110102T050000Z",
506            $blob,
507            new \DateTimeZone('America/Toronto')
508        );
509
510    }
511
512    function testNoValidInstances() {
513
514        // Recurrence rule with no valid instances
515        $blob = <<<ICS
516BEGIN:VCALENDAR
517BEGIN:VEVENT
518UID:foobar
519DTSTART:20110101T100000Z
520DTEND:20110103T120000Z
521RRULE:FREQ=WEEKLY;COUNT=1
522EXDATE:20110101T100000Z
523END:VEVENT
524END:VCALENDAR
525ICS;
526
527        $this->assertFreeBusyReport(
528            "",
529            $blob
530        );
531
532    }
533
534    /**
535     * This VAVAILABILITY object overlaps with the time-range, but we're just
536     * busy the entire time.
537     */
538    function testVAvailabilitySimple() {
539
540        $blob = <<<ICS
541BEGIN:VCALENDAR
542BEGIN:VEVENT
543UID:lalala
544DTSTART:20110101T120000Z
545DTEND:20110101T130000Z
546END:VEVENT
547END:VCALENDAR
548ICS;
549
550        $vavail = <<<ICS
551BEGIN:VCALENDAR
552BEGIN:VAVAILABILITY
553DTSTART:20110101T000000Z
554DTEND:20120101T000000Z
555BEGIN:AVAILABLE
556DTSTART:20110101T000000Z
557DTEND:20110101T010000Z
558END:AVAILABLE
559END:VAVAILABILITY
560END:VCALENDAR
561ICS;
562
563        $this->assertFreeBusyReport(
564            "FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20110101T110000Z/20110101T120000Z\n" .
565            "FREEBUSY:20110101T120000Z/20110101T130000Z\n" .
566            "FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20110101T130000Z/20110103T110000Z",
567            $blob,
568            null,
569            $vavail
570        );
571
572    }
573
574    /**
575     * This VAVAILABILITY object does not overlap at all with the freebusy
576     * report, so it should be ignored.
577     */
578    function testVAvailabilityIrrelevant() {
579
580        $blob = <<<ICS
581BEGIN:VCALENDAR
582BEGIN:VEVENT
583UID:lalala
584DTSTART:20110101T120000Z
585DTEND:20110101T130000Z
586END:VEVENT
587END:VCALENDAR
588ICS;
589
590        $vavail = <<<ICS
591BEGIN:VCALENDAR
592BEGIN:VAVAILABILITY
593DTSTART:20150101T000000Z
594DTEND:20160101T000000Z
595BEGIN:AVAILABLE
596DTSTART:20150101T000000Z
597DTEND:20150101T010000Z
598END:AVAILABLE
599END:VAVAILABILITY
600END:VCALENDAR
601ICS;
602
603        $this->assertFreeBusyReport(
604            "FREEBUSY:20110101T120000Z/20110101T130000Z",
605            $blob,
606            null,
607            $vavail
608        );
609
610    }
611
612    /**
613     * This VAVAILABILITY object has a 9am-5pm AVAILABLE object for office
614     * hours.
615     */
616    function testVAvailabilityOfficeHours() {
617
618        $blob = <<<ICS
619BEGIN:VCALENDAR
620BEGIN:VEVENT
621UID:lalala
622DTSTART:20110101T120000Z
623DTEND:20110101T130000Z
624END:VEVENT
625END:VCALENDAR
626ICS;
627
628        $vavail = <<<ICS
629BEGIN:VCALENDAR
630BEGIN:VAVAILABILITY
631DTSTART:20100101T000000Z
632DTEND:20120101T000000Z
633BUSYTYPE:BUSY-TENTATIVE
634BEGIN:AVAILABLE
635DTSTART:20101213T090000Z
636DTEND:20101213T170000Z
637RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
638END:AVAILABLE
639END:VAVAILABILITY
640END:VCALENDAR
641ICS;
642
643        $this->assertFreeBusyReport(
644            "FREEBUSY;FBTYPE=BUSY-TENTATIVE:20110101T110000Z/20110101T120000Z\n" .
645            "FREEBUSY:20110101T120000Z/20110101T130000Z\n" .
646            "FREEBUSY;FBTYPE=BUSY-TENTATIVE:20110101T130000Z/20110103T090000Z\n",
647            $blob,
648            null,
649            $vavail
650        );
651
652    }
653
654    /**
655     * This test has the same office hours, but has a vacation blocked off for
656     * the relevant time, using a higher priority. (lower number).
657     */
658    function testVAvailabilityOfficeHoursVacation() {
659
660        $blob = <<<ICS
661BEGIN:VCALENDAR
662BEGIN:VEVENT
663UID:lalala
664DTSTART:20110101T120000Z
665DTEND:20110101T130000Z
666END:VEVENT
667END:VCALENDAR
668ICS;
669
670        $vavail = <<<ICS
671BEGIN:VCALENDAR
672BEGIN:VAVAILABILITY
673DTSTART:20100101T000000Z
674DTEND:20120101T000000Z
675BUSYTYPE:BUSY-TENTATIVE
676PRIORITY:2
677BEGIN:AVAILABLE
678DTSTART:20101213T090000Z
679DTEND:20101213T170000Z
680RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
681END:AVAILABLE
682END:VAVAILABILITY
683BEGIN:VAVAILABILITY
684PRIORITY:1
685DTSTART:20101214T000000Z
686DTEND:20110107T000000Z
687BUSYTYPE:BUSY
688END:VAVAILABILITY
689END:VCALENDAR
690ICS;
691
692        $this->assertFreeBusyReport(
693            "FREEBUSY:20110101T110000Z/20110103T110000Z",
694            $blob,
695            null,
696            $vavail
697        );
698
699    }
700
701    /**
702     * This test has the same input as the last, except somebody mixed up the
703     * PRIORITY values.
704     *
705     * The end-result is that the vacation VAVAILABILITY is completely ignored.
706     */
707    function testVAvailabilityOfficeHoursVacation2() {
708
709        $blob = <<<ICS
710BEGIN:VCALENDAR
711BEGIN:VEVENT
712UID:lalala
713DTSTART:20110101T120000Z
714DTEND:20110101T130000Z
715END:VEVENT
716END:VCALENDAR
717ICS;
718
719        $vavail = <<<ICS
720BEGIN:VCALENDAR
721BEGIN:VAVAILABILITY
722DTSTART:20100101T000000Z
723DTEND:20120101T000000Z
724BUSYTYPE:BUSY-TENTATIVE
725PRIORITY:1
726BEGIN:AVAILABLE
727DTSTART:20101213T090000Z
728DTEND:20101213T170000Z
729RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
730END:AVAILABLE
731END:VAVAILABILITY
732BEGIN:VAVAILABILITY
733PRIORITY:2
734DTSTART:20101214T000000Z
735DTEND:20110107T000000Z
736BUSYTYPE:BUSY
737END:VAVAILABILITY
738END:VCALENDAR
739ICS;
740
741        $this->assertFreeBusyReport(
742            "FREEBUSY;FBTYPE=BUSY-TENTATIVE:20110101T110000Z/20110101T120000Z\n" .
743            "FREEBUSY:20110101T120000Z/20110101T130000Z\n" .
744            "FREEBUSY;FBTYPE=BUSY-TENTATIVE:20110101T130000Z/20110103T090000Z\n",
745            $blob,
746            null,
747            $vavail
748        );
749
750    }
751}
752