5e9e8f570d067daf7fcec689cdf5884b84197b6c
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / TypoScript / Parser / TypoScriptParserTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * Test case for \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser
19 */
20 class TypoScriptParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
21
22 /**
23 * @var \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
24 */
25 protected $typoScriptParser = NULL;
26
27 /**
28 * Set up
29 *
30 * @return void
31 */
32 protected function setUp() {
33 $accessibleClassName = $this->buildAccessibleProxy(\TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::class);
34 $this->typoScriptParser = new $accessibleClassName();
35 }
36
37 /**
38 * Data provider for executeValueModifierReturnsModifiedResult
39 *
40 * @return array modifier name, modifier arguments, current value, expected result
41 */
42 public function executeValueModifierDataProvider() {
43 return array(
44 'prependString with string' => array(
45 'prependString',
46 'abc',
47 '!',
48 '!abc'
49 ),
50 'prependString with empty string' => array(
51 'prependString',
52 'foo',
53 '',
54 'foo',
55 ),
56 'appendString with string' => array(
57 'appendString',
58 'abc',
59 '!',
60 'abc!',
61 ),
62 'appendString with empty string' => array(
63 'appendString',
64 'abc',
65 '',
66 'abc',
67 ),
68 'removeString removes simple string' => array(
69 'removeString',
70 'abcdef',
71 'bc',
72 'adef',
73 ),
74 'removeString removes nothing if no match' => array(
75 'removeString',
76 'abcdef',
77 'foo',
78 'abcdef',
79 ),
80 'removeString removes multiple matches' => array(
81 'removeString',
82 'FooBarFoo',
83 'Foo',
84 'Bar',
85 ),
86 'replaceString replaces simple match' => array(
87 'replaceString',
88 'abcdef',
89 'bc|123',
90 'a123def',
91 ),
92 'replaceString replaces simple match with nothing' => array(
93 'replaceString',
94 'abcdef',
95 'bc',
96 'adef',
97 ),
98 'replaceString replaces multiple matches' => array(
99 'replaceString',
100 'FooBarFoo',
101 'Foo|Bar',
102 'BarBarBar',
103 ),
104 'addToList adds at end of existing list' => array(
105 'addToList',
106 '123,456',
107 '789',
108 '123,456,789',
109 ),
110 'addToList adds at end of existing list including white-spaces' => array(
111 'addToList',
112 '123,456',
113 ' 789 , 32 , 12 ',
114 '123,456, 789 , 32 , 12 ',
115 ),
116 'addToList adds nothing' => array(
117 'addToList',
118 '123,456',
119 '',
120 '123,456,', // This result is probably not what we want (appended comma) ... fix it?
121 ),
122 'addToList adds to empty list' => array(
123 'addToList',
124 '',
125 'foo',
126 'foo',
127 ),
128 'removeFromList removes value from list' => array(
129 'removeFromList',
130 '123,456,789,abc',
131 '456',
132 '123,789,abc',
133 ),
134 'removeFromList removes value at beginning of list' => array(
135 'removeFromList',
136 '123,456,abc',
137 '123',
138 '456,abc',
139 ),
140 'removeFromList removes value at end of list' => array(
141 'removeFromList',
142 '123,456,abc',
143 'abc',
144 '123,456',
145 ),
146 'removeFromList removes multiple values from list' => array(
147 'removeFromList',
148 'foo,123,bar,123',
149 '123',
150 'foo,bar',
151 ),
152 'removeFromList removes empty values' => array(
153 'removeFromList',
154 'foo,,bar',
155 '',
156 'foo,bar',
157 ),
158 'uniqueList removes duplicates' => array(
159 'uniqueList',
160 '123,456,abc,456,456',
161 '',
162 '123,456,abc',
163 ),
164 'uniqueList removes duplicate empty list values' => array(
165 'uniqueList',
166 '123,,456,,abc',
167 '',
168 '123,,456,abc',
169 ),
170 'reverseList returns list reversed' => array(
171 'reverseList',
172 '123,456,abc,456',
173 '',
174 '456,abc,456,123',
175 ),
176 'reverseList keeps empty values' => array(
177 'reverseList',
178 ',123,,456,abc,,456',
179 '',
180 '456,,abc,456,,123,',
181 ),
182 'reverseList does not change single element' => array(
183 'reverseList',
184 '123',
185 '',
186 '123',
187 ),
188 'sortList sorts a list' => array(
189 'sortList',
190 '10,100,0,20,abc',
191 '',
192 '0,10,20,100,abc',
193 ),
194 'sortList sorts a list numeric' => array(
195 'sortList',
196 '10,0,100,-20,abc',
197 'numeric',
198 '-20,0,abc,10,100',
199 ),
200 'sortList sorts a list descending' => array(
201 'sortList',
202 '10,100,0,20,abc,-20',
203 'descending',
204 'abc,100,20,10,0,-20',
205 ),
206 'sortList sorts a list numeric descending' => array(
207 'sortList',
208 '10,100,0,20,abc,-20',
209 'descending,numeric',
210 '100,20,10,0,abc,-20',
211 ),
212 'sortList ignores invalid modifier arguments' => array(
213 'sortList',
214 '10,100,20',
215 'foo,descending,bar',
216 '100,20,10',
217 ),
218 );
219 }
220
221 /**
222 * @test
223 * @dataProvider executeValueModifierDataProvider
224 */
225 public function executeValueModifierReturnsModifiedResult($modifierName, $currentValue, $modifierArgument, $expected) {
226 $actualValue = $this->typoScriptParser->_call('executeValueModifier', $modifierName, $modifierArgument, $currentValue);
227 $this->assertEquals($expected, $actualValue);
228 }
229
230 /**
231 * @param string $typoScript
232 * @param array $expected
233 * @dataProvider typoScriptIsParsedToArrayDataProvider
234 * @test
235 */
236 public function typoScriptIsParsedToArray($typoScript, array $expected) {
237 $this->typoScriptParser->parse($typoScript);
238 $this->assertEquals($expected, $this->typoScriptParser->setup);
239 }
240
241 /**
242 * @return array
243 */
244 public function typoScriptIsParsedToArrayDataProvider() {
245 return array(
246 'simple assignment' => array(
247 'key = value',
248 array(
249 'key' => 'value',
250 )
251 ),
252 'simple assignment with escaped dot at the beginning' => array(
253 '\\.key = value',
254 array(
255 '.key' => 'value',
256 )
257 ),
258 'simple assignment with protected escaped dot at the beginning' => array(
259 '\\\\.key = value',
260 array(
261 '\\.' => array(
262 'key' => 'value',
263 ),
264 )
265 ),
266 'nested assignment' => array(
267 'lib.key = value',
268 array(
269 'lib.' => array(
270 'key' => 'value',
271 ),
272 ),
273 ),
274 'nested assignment with escaped key' => array(
275 'lib\\.key = value',
276 array(
277 'lib.key' => 'value',
278 ),
279 ),
280 'nested assignment with escaped key and escaped dot at the beginning' => array(
281 '\\.lib\\.key = value',
282 array(
283 '.lib.key' => 'value',
284 ),
285 ),
286 'nested assignment with protected escaped key' => array(
287 'lib\\\\.key = value',
288 array(
289 'lib\\.' => array('key' => 'value'),
290 ),
291 ),
292 'nested assignment with protected escaped key and protected escaped dot at the beginning' => array(
293 '\\\\.lib\\\\.key = value',
294 array(
295 '\\.' => array(
296 'lib\\.' => array('key' => 'value'),
297 ),
298 ),
299 ),
300 'assignment with escaped an non escaped keys' => array(
301 'firstkey.secondkey\\.thirdkey.setting = value',
302 array(
303 'firstkey.' => array(
304 'secondkey.thirdkey.' => array(
305 'setting' => 'value'
306 )
307 )
308 )
309 ),
310 'nested structured assignment' => array(
311 'lib {' . LF .
312 'key = value' . LF .
313 '}',
314 array(
315 'lib.' => array(
316 'key' => 'value',
317 ),
318 ),
319 ),
320 'nested structured assignment with escaped key inside' => array(
321 'lib {' . LF .
322 'key\\.nextkey = value' . LF .
323 '}',
324 array(
325 'lib.' => array(
326 'key.nextkey' => 'value',
327 ),
328 ),
329 ),
330 'nested structured assignment with escaped key inside and escaped dots at the beginning' => array(
331 '\\.lib {' . LF .
332 '\\.key\\.nextkey = value' . LF .
333 '}',
334 array(
335 '.lib.' => array(
336 '.key.nextkey' => 'value',
337 ),
338 ),
339 ),
340 'nested structured assignment with protected escaped key inside' => array(
341 'lib {' . LF .
342 'key\\\\.nextkey = value' . LF .
343 '}',
344 array(
345 'lib.' => array(
346 'key\\.' => array('nextkey' => 'value'),
347 ),
348 ),
349 ),
350 'nested structured assignment with protected escaped key inside and protected escaped dots at the beginning' => array(
351 '\\\\.lib {' . LF .
352 '\\\\.key\\\\.nextkey = value' . LF .
353 '}',
354 array(
355 '\\.' => array(
356 'lib.' => array(
357 '\\.' => array(
358 'key\\.' => array('nextkey' => 'value'),
359 ),
360 ),
361 ),
362 ),
363 ),
364 'nested structured assignment with escaped key' => array(
365 'lib\\.anotherkey {' . LF .
366 'key = value' . LF .
367 '}',
368 array(
369 'lib.anotherkey.' => array(
370 'key' => 'value',
371 ),
372 ),
373 ),
374 'nested structured assignment with protected escaped key' => array(
375 'lib\\\\.anotherkey {' . LF .
376 'key = value' . LF .
377 '}',
378 array(
379 'lib\\.' => array(
380 'anotherkey.' => array(
381 'key' => 'value',
382 ),
383 ),
384 ),
385 ),
386 'multiline assignment' => array(
387 'key (' . LF .
388 'first' . LF .
389 'second' . LF .
390 ')',
391 array(
392 'key' => 'first' . LF . 'second',
393 ),
394 ),
395 'multiline assignment with escaped key' => array(
396 'key\\.nextkey (' . LF .
397 'first' . LF .
398 'second' . LF .
399 ')',
400 array(
401 'key.nextkey' => 'first' . LF . 'second',
402 ),
403 ),
404 'multiline assignment with protected escaped key' => array(
405 'key\\\\.nextkey (' . LF .
406 'first' . LF .
407 'second' . LF .
408 ')',
409 array(
410 'key\\.' => array('nextkey' => 'first' . LF . 'second'),
411 ),
412 ),
413 'copying values' => array(
414 'lib.default = value' . LF .
415 'lib.copy < lib.default',
416 array(
417 'lib.' => array(
418 'default' => 'value',
419 'copy' => 'value',
420 ),
421 ),
422 ),
423 'copying values with escaped key' => array(
424 'lib\\.default = value' . LF .
425 'lib.copy < lib\\.default',
426 array(
427 'lib.default' => 'value',
428 'lib.' => array(
429 'copy' => 'value',
430 ),
431 ),
432 ),
433 'copying values with protected escaped key' => array(
434 'lib\\\\.default = value' . LF .
435 'lib.copy < lib\\\\.default',
436 array(
437 'lib\\.' => array('default' => 'value'),
438 'lib.' => array(
439 'copy' => 'value',
440 ),
441 ),
442 ),
443 'one-line hash comment' => array(
444 'first = 1' . LF .
445 '# ignore = me' . LF .
446 'second = 2',
447 array(
448 'first' => '1',
449 'second' => '2',
450 ),
451 ),
452 'one-line slash comment' => array(
453 'first = 1' . LF .
454 '// ignore = me' . LF .
455 'second = 2',
456 array(
457 'first' => '1',
458 'second' => '2',
459 ),
460 ),
461 'multi-line slash comment' => array(
462 'first = 1' . LF .
463 '/*' . LF .
464 'ignore = me' . LF .
465 '*/' . LF .
466 'second = 2',
467 array(
468 'first' => '1',
469 'second' => '2',
470 ),
471 ),
472 'nested assignment repeated segment names' => array(
473 'test.test.test = 1',
474 array(
475 'test.' => array(
476 'test.' => array(
477 'test' => '1',
478 ),
479 )
480 ),
481 ),
482 'simple assignment operator with tab character before "="' => array(
483 'test = someValue',
484 array(
485 'test' => 'someValue',
486 ),
487 ),
488 'simple assignment operator character as value "="' => array(
489 'test ==TEST=',
490 array(
491 'test' => '=TEST=',
492 ),
493 ),
494 'nested assignment operator character as value "="' => array(
495 'test.test ==TEST=',
496 array(
497 'test.' => array(
498 'test' => '=TEST=',
499 ),
500 ),
501 ),
502 'simple assignment character as value "<"' => array(
503 'test =<TEST>',
504 array(
505 'test' => '<TEST>',
506 ),
507 ),
508 'nested assignment character as value "<"' => array(
509 'test.test =<TEST>',
510 array(
511 'test.' => array(
512 'test' => '<TEST>',
513 ),
514 ),
515 ),
516 'simple assignment character as value ">"' => array(
517 'test =>TEST<',
518 array(
519 'test' => '>TEST<',
520 ),
521 ),
522 'nested assignment character as value ">"' => array(
523 'test.test =>TEST<',
524 array(
525 'test.' => array(
526 'test' => '>TEST<',
527 ),
528 ),
529 ),
530 'nested assignment repeated segment names with whitespaces' => array(
531 'test.test.test = 1' . " \t",
532 array(
533 'test.' => array(
534 'test.' => array(
535 'test' => '1',
536 ),
537 )
538 ),
539 ),
540 'simple assignment operator character as value "=" with whitespaces' => array(
541 'test = =TEST=' . " \t",
542 array(
543 'test' => '=TEST=',
544 ),
545 ),
546 'nested assignment operator character as value "=" with whitespaces' => array(
547 'test.test = =TEST=' . " \t",
548 array(
549 'test.' => array(
550 'test' => '=TEST=',
551 ),
552 ),
553 ),
554 'simple assignment character as value "<" with whitespaces' => array(
555 'test = <TEST>' . " \t",
556 array(
557 'test' => '<TEST>',
558 ),
559 ),
560 'nested assignment character as value "<" with whitespaces' => array(
561 'test.test = <TEST>' . " \t",
562 array(
563 'test.' => array(
564 'test' => '<TEST>',
565 ),
566 ),
567 ),
568 'simple assignment character as value ">" with whitespaces' => array(
569 'test = >TEST<' . " \t",
570 array(
571 'test' => '>TEST<',
572 ),
573 ),
574 'nested assignment character as value ">" with whitespaces' => array(
575 'test.test = >TEST<',
576 array(
577 'test.' => array(
578 'test' => '>TEST<',
579 ),
580 ),
581 ),
582 'CSC example #1' => array(
583 'linkParams.ATagParams.dataWrap = class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
584 array(
585 'linkParams.' => array(
586 'ATagParams.' => array(
587 'dataWrap' => 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
588 ),
589 ),
590 ),
591 ),
592 'CSC example #2' => array(
593 'linkParams.ATagParams {' . LF .
594 'dataWrap = class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"' . LF .
595 '}',
596 array(
597 'linkParams.' => array(
598 'ATagParams.' => array(
599 'dataWrap' => 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
600 ),
601 ),
602 ),
603 ),
604 'CSC example #3' => array(
605 'linkParams.ATagParams.dataWrap (' . LF .
606 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"' . LF .
607 ')',
608 array(
609 'linkParams.' => array(
610 'ATagParams.' => array(
611 'dataWrap' => 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
612 ),
613 ),
614 ),
615 ),
616 'key with colon' => array(
617 'some:key = is valid',
618 array(
619 'some:key' => 'is valid'
620 )
621 ),
622 'special operator' => array(
623 'some := addToList(a)',
624 array(
625 'some' => 'a'
626 )
627 ),
628 'special operator with white-spaces' => array(
629 'some := addToList (a)',
630 array(
631 'some' => 'a'
632 )
633 ),
634 'special operator with tabs' => array(
635 'some := addToList (a)',
636 array(
637 'some' => 'a'
638 )
639 ),
640 'special operator with white-spaces and tabs in value' => array(
641 'some := addToList( a, b, c )',
642 array(
643 'some' => 'a, b, c'
644 )
645 ),
646 'special operator and colon, no spaces' => array(
647 'some:key:=addToList(a)',
648 array(
649 'some:key' => 'a'
650 )
651 ),
652 'key with all special symbols' => array(
653 'someSpecial\\_:-\\.Chars = is valid',
654 array(
655 'someSpecial\\_:-.Chars' => 'is valid'
656 )
657 ),
658 );
659 }
660
661 /**
662 * @test
663 */
664 public function setValCanBeCalledWithArrayValueParameter() {
665 $string = '';
666 $setup = array();
667 $value = array();
668 $this->typoScriptParser->setVal($string, $setup, $value);
669 }
670
671 /**
672 * @test
673 */
674 public function setValCanBeCalledWithStringValueParameter() {
675 $string = '';
676 $setup = array();
677 $value = '';
678 $this->typoScriptParser->setVal($string, $setup, $value);
679 }
680
681 /**
682 * @test
683 * @dataProvider parseNextKeySegmentReturnsCorrectNextKeySegmentDataProvider
684 */
685 public function parseNextKeySegmentReturnsCorrectNextKeySegment($key, $expectedKeySegment, $expectedRemainingKey) {
686 list($keySegment, $remainingKey) = $this->typoScriptParser->_call('parseNextKeySegment', $key);
687 $this->assertSame($expectedKeySegment, $keySegment);
688 $this->assertSame($expectedRemainingKey, $remainingKey);
689 }
690
691 /**
692 * @return array
693 */
694 public function parseNextKeySegmentReturnsCorrectNextKeySegmentDataProvider() {
695 return array(
696 'key without separator' => array(
697 'testkey',
698 'testkey',
699 ''
700 ),
701 'key with normal separator' => array(
702 'test.key',
703 'test',
704 'key'
705 ),
706 'key with multiple normal separators' => array(
707 'test.key.subkey',
708 'test',
709 'key.subkey'
710 ),
711 'key with separator and escape character' => array(
712 'te\\st.test',
713 'te\\st',
714 'test'
715 ),
716 'key with escaped separators' => array(
717 'test\\.key\\.subkey',
718 'test.key.subkey',
719 ''
720 ),
721 'key with escaped and unescaped separator 1' => array(
722 'test.test\\.key',
723 'test',
724 'test\\.key'
725 ),
726 'key with escaped and unescaped separator 2' => array(
727 'test\\.test.key\\.key2',
728 'test.test',
729 'key\\.key2'
730 ),
731 'key with escaped escape character' => array(
732 'test\\\\.key',
733 'test\\',
734 'key'
735 ),
736 'key with escaped separator and additional escape character' => array(
737 'test\\\\\\.key',
738 'test\\\\',
739 'key'
740 ),
741
742 'multiple escape characters within the key are preserved' => array(
743 'te\\\\st\\\\.key',
744 'te\\\\st\\',
745 'key'
746 )
747 );
748 }
749
750 }