VCalendarTest.php 13 KB

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