VCalendarTest.php 13 KB

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