[FEATURE] Support feature toggle in expression language
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Configuration / TypoScript / ConditionMatching / AbstractConditionMatcherTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Core\Tests\Unit\Configuration\TypoScript\ConditionMatching;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Prophecy\Argument;
19 use TYPO3\CMS\Backend\Configuration\TypoScript\ConditionMatching\ConditionMatcher;
20 use TYPO3\CMS\Core\Cache\CacheManager;
21 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
22 use TYPO3\CMS\Core\Configuration\TypoScript\ConditionMatching\AbstractConditionMatcher;
23 use TYPO3\CMS\Core\Context\Context;
24 use TYPO3\CMS\Core\Context\DateTimeAspect;
25 use TYPO3\CMS\Core\Core\ApplicationContext;
26 use TYPO3\CMS\Core\Http\ServerRequest;
27 use TYPO3\CMS\Core\Log\Logger;
28 use TYPO3\CMS\Core\Package\PackageInterface;
29 use TYPO3\CMS\Core\Package\PackageManager;
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
32
33 /**
34 * Test cases
35 */
36 class AbstractConditionMatcherTest extends UnitTestCase
37 {
38 /**
39 * @var ApplicationContext
40 */
41 protected $backupApplicationContext;
42
43 /**
44 * @var AbstractConditionMatcher|\PHPUnit_Framework_MockObject_MockObject
45 */
46 protected $conditionMatcher;
47
48 /**
49 * @var \ReflectionMethod
50 */
51 protected $evaluateConditionCommonMethod;
52
53 /**
54 * @var \ReflectionMethod
55 */
56 protected $evaluateExpressionMethod;
57
58 /**
59 * Set up
60 */
61 protected function setUp(): void
62 {
63 require_once 'Fixtures/ConditionMatcherUserFuncs.php';
64
65 $this->resetSingletonInstances = true;
66 $GLOBALS['TYPO3_REQUEST'] = new ServerRequest();
67 $cacheFrontendProphecy = $this->prophesize(FrontendInterface::class);
68 $cacheFrontendProphecy->has(Argument::any())->willReturn(false);
69 $cacheFrontendProphecy->set(Argument::any(), Argument::any())->willReturn(null);
70 $cacheManagerProphecy = $this->prophesize(CacheManager::class);
71 $cacheManagerProphecy->getCache('cache_core')->willReturn($cacheFrontendProphecy->reveal());
72 GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManagerProphecy->reveal());
73
74 $packageManagerProphecy = $this->prophesize(PackageManager::class);
75 $corePackageProphecy = $this->prophesize(PackageInterface::class);
76 $corePackageProphecy->getPackagePath()->willReturn(__DIR__ . '/../../../../../../../sysext/core/');
77 $packageManagerProphecy->getActivePackages()->willReturn([
78 $corePackageProphecy->reveal()
79 ]);
80 GeneralUtility::setSingletonInstance(PackageManager::class, $packageManagerProphecy->reveal());
81
82 $this->initConditionMatcher();
83 $this->backupApplicationContext = GeneralUtility::getApplicationContext();
84 }
85
86 protected function initConditionMatcher()
87 {
88 // test the abstract methods via the backend condition matcher
89 $this->conditionMatcher = $this->getAccessibleMock(ConditionMatcher::class, ['determineRootline']);
90 $this->evaluateConditionCommonMethod = new \ReflectionMethod(AbstractConditionMatcher::class, 'evaluateConditionCommon');
91 $this->evaluateConditionCommonMethod->setAccessible(true);
92 $this->evaluateExpressionMethod = new \ReflectionMethod(AbstractConditionMatcher::class, 'evaluateExpression');
93 $this->evaluateExpressionMethod->setAccessible(true);
94 $loggerProphecy = $this->prophesize(Logger::class);
95 $this->conditionMatcher->setLogger($loggerProphecy->reveal());
96 }
97
98 /**
99 * Tear down
100 */
101 protected function tearDown(): void
102 {
103 Fixtures\GeneralUtilityFixture::setApplicationContext($this->backupApplicationContext);
104 parent::tearDown();
105 }
106
107 /**
108 * @return array
109 */
110 public function datesConditionDataProvider(): array
111 {
112 return [
113 '[dayofmonth = 17]' => ['dayofmonth', 17, true],
114 '[dayofweek = 3]' => ['dayofweek', 3, true],
115 '[dayofyear = 16]' => ['dayofyear', 16, true],
116 '[hour = 11]' => ['hour', 11, true],
117 '[minute = 4]' => ['minute', 4, true],
118 '[month = 1]' => ['month', 1, true],
119 '[year = 1945]' => ['year', 1945, true],
120 ];
121 }
122
123 /**
124 * @test
125 * @dataProvider datesConditionDataProvider
126 * @param string $expressionMethod
127 * @param int $expressionValue
128 * @param bool $expected
129 */
130 public function checkConditionMatcherForDates(string $expressionMethod, int $expressionValue, bool $expected): void
131 {
132 $GLOBALS['SIM_EXEC_TIME'] = mktime(11, 4, 0, 1, 17, 1945);
133 $this->assertSame($expected, $this->evaluateConditionCommonMethod->invokeArgs(
134 $this->conditionMatcher,
135 [$expressionMethod, $expressionValue]
136 ));
137 }
138
139 /**
140 * @return array
141 */
142 public function datesFunctionDataProvider(): array
143 {
144 return [
145 '[dayofmonth = 17]' => ['j', 17, true],
146 '[dayofweek = 3]' => ['w', 3, true],
147 '[dayofyear = 16]' => ['z', 16, true],
148 '[hour = 11]' => ['G', 11, true],
149 '[minute = 4]' => ['i', 4, true],
150 '[month = 1]' => ['n', 1, true],
151 '[year = 1945]' => ['Y', 1945, true],
152 ];
153 }
154
155 /**
156 * @test
157 * @dataProvider datesFunctionDataProvider
158 * @param string $format
159 * @param int $expressionValue
160 * @param bool $expected
161 */
162 public function checkConditionMatcherForDateFunction(string $format, int $expressionValue, bool $expected): void
163 {
164 $GLOBALS['SIM_EXEC_TIME'] = gmmktime(11, 4, 0, 1, 17, 1945);
165 GeneralUtility::makeInstance(Context::class)
166 ->setAspect('date', new DateTimeAspect(new \DateTimeImmutable('@' . $GLOBALS['SIM_EXEC_TIME'])));
167 $this->assertSame(
168 $expected,
169 $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['date("' . $format . '") == ' . $expressionValue])
170 );
171 }
172
173 /**
174 * @test
175 */
176 public function checkConditionMatcherForFeatureFunction(): void
177 {
178 $featureName = 'test.testFeature';
179 $GLOBALS['TYPO3_CONF_VARS']['SYS']['features'][$featureName] = true;
180 $this->assertTrue(
181 $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['feature("' . $featureName . '")'])
182 );
183 $this->assertTrue(
184 $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['feature("' . $featureName . '") == true'])
185 );
186 $this->assertTrue(
187 $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['feature("' . $featureName . '") === true'])
188 );
189 $this->assertFalse(
190 $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['feature("' . $featureName . '") == false'])
191 );
192 $this->assertFalse(
193 $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['feature("' . $featureName . '") === false'])
194 );
195
196 $GLOBALS['TYPO3_CONF_VARS']['SYS']['features'][$featureName] = false;
197 $this->assertFalse(
198 $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['feature("' . $featureName . '")'])
199 );
200 $this->assertFalse(
201 $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['feature("' . $featureName . '") == true'])
202 );
203 $this->assertFalse(
204 $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['feature("' . $featureName . '") === true'])
205 );
206 $this->assertTrue(
207 $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['feature("' . $featureName . '") == false'])
208 );
209 $this->assertTrue(
210 $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['feature("' . $featureName . '") === false'])
211 );
212 }
213
214 /**
215 * @return array
216 */
217 public function hostnameDataProvider(): array
218 {
219 return [
220 '[hostname = localhost]' => ['hostname', 'localhost', true],
221 '[hostname = localhost, foo.local]' => ['hostname', 'localhost, foo.local', true],
222 '[hostname = bar.local, foo.local]' => ['hostname', 'bar.local, foo.local', false],
223 ];
224 }
225
226 /**
227 * @test
228 * @dataProvider hostnameDataProvider
229 * @param string $expressionMethod
230 * @param string $expressionValue
231 * @param bool $expected
232 */
233 public function checkConditionMatcherForHostname(string $expressionMethod, string $expressionValue, bool $expected): void
234 {
235 $GLOBALS['_SERVER']['REMOTE_ADDR'] = '127.0.0.1';
236 $this->assertSame($expected, $this->evaluateConditionCommonMethod->invokeArgs(
237 $this->conditionMatcher,
238 [$expressionMethod, $expressionValue]
239 ));
240 }
241
242 /**
243 * Data provider with matching applicationContext conditions.
244 *
245 * @return array
246 */
247 public function matchingApplicationContextConditionsDataProvider(): array
248 {
249 return [
250 ['Production*'],
251 ['Production/Staging/*'],
252 ['Production/Staging/Server2'],
253 ['/^Production.*$/'],
254 ['/^Production\\/.+\\/Server\\d+$/'],
255 ];
256 }
257
258 /**
259 * @test
260 * @dataProvider matchingApplicationContextConditionsDataProvider
261 */
262 public function evaluateConditionCommonReturnsTrueForMatchingContexts($matchingContextCondition): void
263 {
264 /** @var ApplicationContext $applicationContext */
265 $applicationContext = new ApplicationContext('Production/Staging/Server2');
266 Fixtures\GeneralUtilityFixture::setApplicationContext($applicationContext);
267
268 $this->assertTrue(
269 $this->evaluateConditionCommonMethod->invokeArgs($this->conditionMatcher, ['applicationContext', $matchingContextCondition])
270 );
271 // Test expression language
272 $this->assertTrue(
273 $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['like("' . $applicationContext . '", "' . preg_quote($matchingContextCondition, '/') . '")'])
274 );
275 }
276
277 /**
278 * Data provider with not matching applicationContext conditions.
279 *
280 * @return array
281 */
282 public function notMatchingApplicationContextConditionsDataProvider(): array
283 {
284 return [
285 ['Production'],
286 ['Testing*'],
287 ['Development/Profiling, Testing/Unit'],
288 ['Testing/Staging/Server2'],
289 ['/^Testing.*$/'],
290 ['/^Production\\/.+\\/Host\\d+$/'],
291 ];
292 }
293
294 /**
295 * @test
296 * @dataProvider notMatchingApplicationContextConditionsDataProvider
297 */
298 public function evaluateConditionCommonReturnsNullForNotMatchingApplicationContexts($notMatchingApplicationContextCondition): void
299 {
300 /** @var ApplicationContext $applicationContext */
301 $applicationContext = new ApplicationContext('Production/Staging/Server2');
302 Fixtures\GeneralUtilityFixture::setApplicationContext($applicationContext);
303
304 $this->assertFalse(
305 $this->evaluateConditionCommonMethod->invokeArgs($this->conditionMatcher, ['applicationContext', $notMatchingApplicationContextCondition])
306 );
307 // Test expression language
308 $this->assertFalse(
309 $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['like("' . $applicationContext . '", "' . preg_quote($notMatchingApplicationContextCondition, '/') . '")'])
310 );
311 }
312
313 /**
314 * Data provider for evaluateConditionCommonEvaluatesIpAddressesCorrectly
315 *
316 * @return array
317 */
318 public function evaluateConditionCommonDevIpMaskDataProvider(): array
319 {
320 return [
321 // [0] $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']
322 // [1] Actual IP
323 // [2] Expected condition result
324 'IP matches' => [
325 '127.0.0.1',
326 '127.0.0.1',
327 true,
328 ],
329 'ipv4 wildcard subnet' => [
330 '127.0.0.1/24',
331 '127.0.0.2',
332 true,
333 ],
334 'ipv6 wildcard subnet' => [
335 '0:0::1/128',
336 '::1',
337 true,
338 ],
339 'List of addresses matches' => [
340 '1.2.3.4, 5.6.7.8',
341 '5.6.7.8',
342 true,
343 ],
344 'IP does not match' => [
345 '127.0.0.1',
346 '127.0.0.2',
347 false,
348 ],
349 'ipv4 subnet does not match' => [
350 '127.0.0.1/8',
351 '126.0.0.1',
352 false,
353 ],
354 'ipv6 subnet does not match' => [
355 '::1/127',
356 '::2',
357 false
358 ],
359 'List of addresses does not match' => [
360 '127.0.0.1, ::1',
361 '::2',
362 false,
363 ],
364 ];
365 }
366
367 /**
368 * @test
369 * @dataProvider evaluateConditionCommonDevIpMaskDataProvider
370 */
371 public function evaluateConditionCommonEvaluatesIpAddressesCorrectly($devIpMask, $actualIp, $expectedResult): void
372 {
373 // Do not trigger proxy stuff of GeneralUtility::getIndPEnv
374 unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP']);
375
376 GeneralUtility::setIndpEnv('REMOTE_ADDR', $actualIp);
377 $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] = $devIpMask;
378 $this->initConditionMatcher();
379 $result = $this->evaluateExpressionMethod->invokeArgs($this->conditionMatcher, ['ip("devIP")']);
380 $this->assertSame($expectedResult, $result);
381 }
382
383 /**
384 * @test
385 */
386 public function testUserFuncIsCalled(): void
387 {
388 $this->assertTrue(
389 $this->evaluateConditionCommonMethod->invokeArgs(
390 $this->conditionMatcher,
391 ['userFunc', 'user_testFunction']
392 )
393 );
394 }
395
396 /**
397 * @test
398 */
399 public function testUserFuncWithSingleArgument(): void
400 {
401 $this->assertTrue(
402 $this->evaluateConditionCommonMethod->invokeArgs(
403 $this->conditionMatcher,
404 ['userFunc', 'user_testFunctionWithSingleArgument(x)']
405 )
406 );
407 }
408
409 /**
410 * @test
411 */
412 public function testUserFuncWithIntegerZeroArgument(): void
413 {
414 $this->assertTrue(
415 $this->evaluateConditionCommonMethod->invokeArgs(
416 $this->conditionMatcher,
417 ['userFunc', 'user_testFunctionWithSingleArgument(0)']
418 )
419 );
420 }
421
422 /**
423 * @test
424 */
425 public function testUserFuncWithWhitespaceArgument(): void
426 {
427 $this->assertTrue(
428 $this->evaluateConditionCommonMethod->invokeArgs(
429 $this->conditionMatcher,
430 ['userFunc', 'user_testFunctionWithNoArgument( )']
431 )
432 );
433 }
434
435 /**
436 * @test
437 */
438 public function testUserFuncWithMultipleArguments(): void
439 {
440 $this->assertTrue(
441 $this->evaluateConditionCommonMethod->invokeArgs(
442 $this->conditionMatcher,
443 ['userFunc', 'user_testFunctionWithThreeArguments(1,2,3)']
444 )
445 );
446 }
447
448 /**
449 * @test
450 */
451 public function testUserFuncWithMultipleDifferentArgumentsNullBoolString(): void
452 {
453 $this->assertTrue(
454 $this->evaluateConditionCommonMethod->invokeArgs(
455 $this->conditionMatcher,
456 ['userFunc', 'user_testFunctionWithThreeArguments(0,true,"foo")']
457 )
458 );
459 }
460
461 /**
462 * @test
463 */
464 public function testUserFuncWithMultipleDifferentArgumentsNullStringBool(): void
465 {
466 $this->assertTrue(
467 $this->evaluateConditionCommonMethod->invokeArgs(
468 $this->conditionMatcher,
469 ['userFunc', 'user_testFunctionWithThreeArguments(0,"foo",true)']
470 )
471 );
472 }
473
474 /**
475 * @test
476 */
477 public function testUserFuncWithMultipleDifferentArgumentsStringBoolNull(): void
478 {
479 $this->assertTrue(
480 $this->evaluateConditionCommonMethod->invokeArgs(
481 $this->conditionMatcher,
482 ['userFunc', 'user_testFunctionWithThreeArguments("foo",true,0)']
483 )
484 );
485 }
486
487 /**
488 * @test
489 */
490 public function testUserFuncWithMultipleDifferentArgumentsStringNullBool(): void
491 {
492 $this->assertTrue(
493 $this->evaluateConditionCommonMethod->invokeArgs(
494 $this->conditionMatcher,
495 ['userFunc', 'user_testFunctionWithThreeArguments("foo",0,true)']
496 )
497 );
498 }
499
500 /**
501 * @test
502 */
503 public function testUserFuncWithMultipleDifferentArgumentsBoolNullString(): void
504 {
505 $this->assertTrue(
506 $this->evaluateConditionCommonMethod->invokeArgs(
507 $this->conditionMatcher,
508 ['userFunc', 'user_testFunctionWithThreeArguments(true,0,"foo")']
509 )
510 );
511 }
512
513 /**
514 * @test
515 */
516 public function testUserFuncWithMultipleDifferentArgumentsBoolStringNull(): void
517 {
518 $this->assertTrue(
519 $this->evaluateConditionCommonMethod->invokeArgs(
520 $this->conditionMatcher,
521 ['userFunc', 'user_testFunctionWithThreeArguments(true,"foo",0)']
522 )
523 );
524 }
525
526 /**
527 * @test
528 */
529 public function testUserFuncWithMultipleDifferentArgumentsNullBoolStringSingleQuotes(): void
530 {
531 $this->assertTrue(
532 $this->evaluateConditionCommonMethod->invokeArgs(
533 $this->conditionMatcher,
534 ['userFunc', "user_testFunctionWithThreeArguments(0,true,'foo')"]
535 )
536 );
537 }
538
539 /**
540 * @test
541 */
542 public function testUserFuncWithMultipleDifferentArgumentsNullStringBoolSingleQuotes(): void
543 {
544 $this->assertTrue(
545 $this->evaluateConditionCommonMethod->invokeArgs(
546 $this->conditionMatcher,
547 ['userFunc', "user_testFunctionWithThreeArguments(0,'foo',true)"]
548 )
549 );
550 }
551
552 /**
553 * @test
554 */
555 public function testUserFuncWithMultipleDifferentArgumentsStringBoolNullSingleQuotes(): void
556 {
557 $this->assertTrue(
558 $this->evaluateConditionCommonMethod->invokeArgs(
559 $this->conditionMatcher,
560 ['userFunc', "user_testFunctionWithThreeArguments('foo',true,0)"]
561 )
562 );
563 }
564
565 /**
566 * @test
567 */
568 public function testUserFuncWithMultipleDifferentArgumentsStringNullBoolSingleQuotes(): void
569 {
570 $this->assertTrue(
571 $this->evaluateConditionCommonMethod->invokeArgs(
572 $this->conditionMatcher,
573 ['userFunc', "user_testFunctionWithThreeArguments('foo',0,true)"]
574 )
575 );
576 }
577
578 /**
579 * @test
580 */
581 public function testUserFuncWithMultipleDifferentArgumentsBoolNullStringSingleQuotes(): void
582 {
583 $this->assertTrue(
584 $this->evaluateConditionCommonMethod->invokeArgs(
585 $this->conditionMatcher,
586 ['userFunc', "user_testFunctionWithThreeArguments(true,0,'foo')"]
587 )
588 );
589 }
590
591 /**
592 * @test
593 */
594 public function testUserFuncWithMultipleDifferentArgumentsBoolStringNullSingleQuotes(): void
595 {
596 $this->assertTrue(
597 $this->evaluateConditionCommonMethod->invokeArgs(
598 $this->conditionMatcher,
599 ['userFunc', "user_testFunctionWithThreeArguments(true,'foo',0)"]
600 )
601 );
602 }
603
604 /**
605 * @test
606 */
607 public function testUserFuncWithMultipleSingleQuotedArguments(): void
608 {
609 $this->assertTrue(
610 $this->evaluateConditionCommonMethod->invokeArgs(
611 $this->conditionMatcher,
612 ['userFunc', "user_testFunctionWithThreeArguments('foo','bar', 'baz')"]
613 )
614 );
615 }
616
617 /**
618 * @test
619 */
620 public function testUserFuncWithMultipleSoubleQuotedArguments(): void
621 {
622 $this->assertTrue(
623 $this->evaluateConditionCommonMethod->invokeArgs(
624 $this->conditionMatcher,
625 ['userFunc', 'user_testFunctionWithThreeArguments("foo","bar","baz")']
626 )
627 );
628 }
629
630 /**
631 * @test
632 */
633 public function testUserFuncReturnsFalse(): void
634 {
635 $this->assertFalse(
636 $this->evaluateConditionCommonMethod->invokeArgs(
637 $this->conditionMatcher,
638 ['userFunc', 'user_testFunctionFalse']
639 )
640 );
641 }
642
643 /**
644 * @test
645 */
646 public function testUserFuncWithMultipleArgumentsAndQuotes(): void
647 {
648 $this->assertTrue(
649 $this->evaluateConditionCommonMethod->invokeArgs(
650 $this->conditionMatcher,
651 ['userFunc', 'user_testFunctionWithThreeArguments(1,2,"3,4,5,6")']
652 )
653 );
654 }
655
656 /**
657 * @test
658 */
659 public function testUserFuncWithMultipleArgumentsAndQuotesAndSpaces(): void
660 {
661 $this->assertTrue(
662 $this->evaluateConditionCommonMethod->invokeArgs(
663 $this->conditionMatcher,
664 ['userFunc', 'user_testFunctionWithThreeArguments ( 1 , 2, "3, 4, 5, 6" )']
665 )
666 );
667 }
668
669 /**
670 * @test
671 */
672 public function testUserFuncWithMultipleArgumentsAndQuotesAndSpacesStripped(): void
673 {
674 $this->assertTrue(
675 $this->evaluateConditionCommonMethod->invokeArgs(
676 $this->conditionMatcher,
677 ['userFunc', 'user_testFunctionWithThreeArgumentsSpaces ( 1 , 2, "3, 4, 5, 6" )']
678 )
679 );
680 }
681
682 /**
683 * @test
684 */
685 public function testUserFuncWithSpacesInQuotes(): void
686 {
687 $this->assertTrue(
688 $this->evaluateConditionCommonMethod->invokeArgs(
689 $this->conditionMatcher,
690 ['userFunc', 'user_testFunctionWithSpaces(" 3, 4, 5, 6 ")']
691 )
692 );
693 }
694
695 /**
696 * @test
697 */
698 public function testUserFuncWithMultipleArgumentsAndQuotesAndSpacesStrippedAndEscapes(): void
699 {
700 $this->assertTrue(
701 $this->evaluateConditionCommonMethod->invokeArgs(
702 $this->conditionMatcher,
703 ['userFunc', 'user_testFunctionWithThreeArgumentsSpaces ( 1 , 2, "3, \"4, 5\", 6" )']
704 )
705 );
706 }
707
708 /**
709 * @test
710 */
711 public function testUserFuncWithQuoteMissing(): void
712 {
713 $this->assertTrue(
714 $this->evaluateConditionCommonMethod->invokeArgs(
715 $this->conditionMatcher,
716 ['userFunc', 'user_testFunctionWithQuoteMissing ("value \")']
717 )
718 );
719 }
720
721 /**
722 * @test
723 */
724 public function testUserFuncWithQuotesInside(): void
725 {
726 $this->assertTrue(
727 $this->evaluateConditionCommonMethod->invokeArgs(
728 $this->conditionMatcher,
729 ['userFunc', 'user_testQuotes("1 \" 2")']
730 )
731 );
732 }
733
734 /**
735 * @test
736 */
737 public function testUserFuncWithClassMethodCall(): void
738 {
739 $this->assertTrue(
740 $this->evaluateConditionCommonMethod->invokeArgs(
741 $this->conditionMatcher,
742 ['userFunc', 'ConditionMatcherUserFunctions::isTrue(1)']
743 )
744 );
745 }
746 }