[TASK] Move all marker-based logic from cObj to MarkerBasedTemplateService
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Service / DependencyOrderingServiceTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Service;
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 use TYPO3\CMS\Core\Service\DependencyOrderingService;
18
19 /**
20 * Test case
21 */
22 class DependencyOrderingServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
23 {
24 /**
25 * @test
26 * @dataProvider orderByDependenciesBuildsCorrectOrderDataProvider
27 * @param array $items
28 * @param string $beforeKey
29 * @param string $afterKey
30 * @param array $expectedOrderedItems
31 */
32 public function orderByDependenciesBuildsCorrectOrder(array $items, $beforeKey, $afterKey, array $expectedOrderedItems)
33 {
34 $orderedItems = (new DependencyOrderingService())->orderByDependencies($items, $beforeKey, $afterKey);
35 $this->assertSame($expectedOrderedItems, $orderedItems);
36 }
37
38 /**
39 * @return array
40 */
41 public function orderByDependenciesBuildsCorrectOrderDataProvider()
42 {
43 return [
44 'unordered' => [
45 [ // $items
46 1 => [],
47 2 => [],
48 ],
49 'before',
50 'after',
51 [ // $expectedOrderedItems
52 1 => [],
53 2 => [],
54 ]
55 ],
56 'ordered' => [
57 [ // $items
58 1 => [],
59 2 => [
60 'precedes' => [ 1 ]
61 ],
62 ],
63 'precedes',
64 'after',
65 [ // $expectedOrderedItems
66 2 => [
67 'precedes' => [ 1 ]
68 ],
69 1 => [],
70 ]
71 ],
72 'mixed' => [
73 [ // $items
74 1 => [],
75 2 => [
76 'before' => [ 1 ]
77 ],
78 3 => [
79 'otherProperty' => true
80 ]
81 ],
82 'before',
83 'after',
84 [ // $expectedOrderedItems
85 2 => [
86 'before' => [ 1 ]
87 ],
88 1 => [],
89 3 => [
90 'otherProperty' => true
91 ],
92 ]
93 ],
94 'reference to non-existing' => [
95 [ // $items
96 2 => [
97 'before' => [ 1 ],
98 'depends' => [ 3 ]
99 ],
100 3 => [
101 'otherProperty' => true
102 ]
103 ],
104 'before',
105 'depends',
106 [ // $expectedOrderedItems
107 3 => [
108 'otherProperty' => true
109 ],
110 2 => [
111 'before' => [ 1 ],
112 'depends' => [ 3 ]
113 ],
114 ]
115 ],
116 'multiple dependencies' => [
117 [ // $items
118 1 => [
119 'depends' => [ 3, 2, 4 ],
120 ],
121 2 => [],
122 3 => [
123 'depends' => [ 2 ],
124 ],
125 ],
126 'before',
127 'depends',
128 [ // $expectedOrderedItems
129 2 => [],
130 3 => [
131 'depends' => [ 2 ],
132 ],
133 1 => [
134 'depends' => [ 3, 2, 4 ],
135 ],
136 ],
137 ],
138 'direct dependency is moved up' => [
139 [ // $items
140 1 => [],
141 2 => [],
142 3 => [
143 'depends' => [ 1 ],
144 ],
145 ],
146 'before',
147 'depends',
148 [ // $expectedOrderedItems
149 1 => [],
150 3 => [
151 'depends' => [ 1 ],
152 ],
153 2 => [],
154 ],
155 ],
156 ];
157 }
158
159 /**
160 * @test
161 * @dataProvider prepareDependenciesBuildsFullIdentifierListDataProvider
162 * @param array $dependencies
163 * @param $expectedDependencies
164 * @throws \InvalidArgumentException
165 */
166 public function prepareDependenciesBuildsFullIdentifierList(array $dependencies, array $expectedDependencies)
167 {
168 /** @var DependencyOrderingService|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $dependencyOrderingService */
169 $dependencyOrderingService = $this->getAccessibleMock(DependencyOrderingService::class, ['dummy']);
170 $preparedDependencies = $dependencyOrderingService->_call('prepareDependencies', $dependencies);
171 $this->assertEquals($expectedDependencies, $preparedDependencies);
172 }
173
174 /**
175 * @return array
176 */
177 public function prepareDependenciesBuildsFullIdentifierListDataProvider()
178 {
179 return [
180 'simple' => [
181 [ // $dependencies
182 1 => [
183 'before' => [],
184 'after' => [ 2 ]
185 ],
186 ],
187 [ // $expectedDependencies
188 1 => [
189 'before' => [],
190 'after' => [ 2 ]
191 ],
192 2 => [
193 'before' => [],
194 'after' => [],
195 ]
196 ]
197 ],
198 'missing before' => [
199 [ // $dependencies
200 1 => [
201 'after' => [ 2 ]
202 ],
203 ],
204 [ // $expectedDependencies
205 1 => [
206 'before' => [],
207 'after' => [ 2 ]
208 ],
209 2 => [
210 'before' => [],
211 'after' => [],
212 ]
213 ]
214 ],
215 ];
216 }
217
218 /**
219 * @test
220 * @dataProvider buildDependencyGraphBuildsValidGraphDataProvider
221 * @param array $dependencies
222 * @param array $expectedGraph
223 */
224 public function buildDependencyGraphBuildsValidGraph(array $dependencies, array $expectedGraph)
225 {
226 $graph = (new DependencyOrderingService())->buildDependencyGraph($dependencies);
227 $this->assertEquals($expectedGraph, $graph);
228 }
229
230 /**
231 * @return array
232 */
233 public function buildDependencyGraphBuildsValidGraphDataProvider()
234 {
235 return [
236 'graph1' => [
237 [ // dependencies
238 1 => [
239 'before' => [],
240 'after' => [ 2 ]
241 ],
242 ],
243 [ // graph
244 1 => [
245 1 => false,
246 2 => true
247 ],
248 2 => [
249 1 => false,
250 2 => false
251 ],
252 ]
253 ],
254 'graph2' => [
255 [ // dependencies
256 1 => [
257 'before' => [ 3 ],
258 'after' => [ 2 ]
259 ],
260 ],
261 [ // graph
262 1 => [
263 1 => false,
264 2 => true,
265 3 => false,
266 ],
267 2 => [
268 1 => false,
269 2 => false,
270 3 => false,
271 ],
272 3 => [
273 1 => true,
274 2 => false,
275 3 => false,
276 ],
277 ]
278 ],
279 'graph3' => [
280 [ // dependencies
281 3 => [
282 'before' => [],
283 'after' => []
284 ],
285 1 => [
286 'before' => [ 3 ],
287 'after' => [ 2 ]
288 ],
289 2 => [
290 'before' => [ 3 ],
291 'after' => []
292 ]
293 ],
294 [ // graph
295 1 => [
296 1 => false,
297 2 => true,
298 3 => false,
299 ],
300 2 => [
301 1 => false,
302 2 => false,
303 3 => false,
304 ],
305 3 => [
306 1 => true,
307 2 => true,
308 3 => false,
309 ],
310 ]
311 ],
312 'cyclic graph' => [
313 [ // dependencies
314 1 => [
315 'before' => [ 2 ],
316 'after' => []
317 ],
318 2 => [
319 'before' => [ 1 ],
320 'after' => []
321 ]
322 ],
323 [ // graph
324 1 => [
325 1 => false,
326 2 => true,
327 ],
328 2 => [
329 1 => true,
330 2 => false,
331 ],
332 ]
333 ],
334 'TYPO3 Flow Packages' => [
335 [ // dependencies
336 'TYPO3.Flow' => [
337 'before' => [],
338 'after' => ['Symfony.Component.Yaml', 'Doctrine.Common', 'Doctrine.DBAL', 'Doctrine.ORM']
339 ],
340 'Doctrine.ORM' => [
341 'before' => [],
342 'after' => ['Doctrine.Common', 'Doctrine.DBAL']
343 ],
344 'Doctrine.Common' => [
345 'before' => [],
346 'after' => []
347 ],
348 'Doctrine.DBAL' => [
349 'before' => [],
350 'after' => ['Doctrine.Common']
351 ],
352 'Symfony.Component.Yaml' => [
353 'before' => [],
354 'after' => []
355 ],
356 ],
357 [ // graph
358 'TYPO3.Flow' => [
359 'TYPO3.Flow' => false,
360 'Doctrine.ORM' => true,
361 'Doctrine.Common' => true,
362 'Doctrine.DBAL' => true,
363 'Symfony.Component.Yaml' => true,
364 ],
365 'Doctrine.ORM' => [
366 'TYPO3.Flow' => false,
367 'Doctrine.ORM' => false,
368 'Doctrine.Common' => true,
369 'Doctrine.DBAL' => true,
370 'Symfony.Component.Yaml' => false,
371 ],
372 'Doctrine.Common' => [
373 'TYPO3.Flow' => false,
374 'Doctrine.ORM' => false,
375 'Doctrine.Common' => false,
376 'Doctrine.DBAL' => false,
377 'Symfony.Component.Yaml' => false,
378 ],
379 'Doctrine.DBAL' => [
380 'TYPO3.Flow' => false,
381 'Doctrine.ORM' => false,
382 'Doctrine.Common' => true,
383 'Doctrine.DBAL' => false,
384 'Symfony.Component.Yaml' => false,
385 ],
386 'Symfony.Component.Yaml' => [
387 'TYPO3.Flow' => false,
388 'Doctrine.ORM' => false,
389 'Doctrine.Common' => false,
390 'Doctrine.DBAL' => false,
391 'Symfony.Component.Yaml' => false,
392 ],
393 ],
394 ],
395 'TYPO3 CMS Extensions' => [
396 [ // dependencies
397 'core' => [
398 'before' => [],
399 'after' => [],
400 ],
401 'openid' => [
402 'before' => [],
403 'after' => ['core', 'setup']
404 ],
405 'scheduler' => [
406 'before' => [],
407 'after' => ['core'],
408 ],
409 'setup' => [
410 'before' => [],
411 'after' => ['core'],
412 ],
413 'sv' => [
414 'before' => [],
415 'after' => ['core'],
416 ],
417 ],
418 [ // graph
419 'core' => [
420 'core' => false,
421 'setup' => false,
422 'sv' => false,
423 'scheduler' => false,
424 'openid' => false,
425 ],
426 'openid' => [
427 'core' => true,
428 'setup' => true,
429 'sv' => false,
430 'scheduler' => false,
431 'openid' => false,
432 ],
433 'scheduler' => [
434 'core' => true,
435 'setup' => false,
436 'sv' => false,
437 'scheduler' => false,
438 'openid' => false,
439 ],
440 'setup' => [
441 'core' => true,
442 'setup' => false,
443 'sv' => false,
444 'scheduler' => false,
445 'openid' => false,
446 ],
447 'sv' => [
448 'core' => true,
449 'setup' => false,
450 'sv' => false,
451 'scheduler' => false,
452 'openid' => false,
453 ],
454 ],
455 ],
456 'Dummy Packages' => [
457 [ // dependencies
458 'A' => [
459 'before' => [],
460 'after' => ['B', 'D', 'C'],
461 ],
462 'B' => [
463 'before' => [],
464 'after' => []
465 ],
466 'C' => [
467 'before' => [],
468 'after' => ['E']
469 ],
470 'D' => [
471 'before' => [],
472 'after' => ['E'],
473 ],
474 'E' => [
475 'before' => [],
476 'after' => [],
477 ],
478 'F' => [
479 'before' => [],
480 'after' => [],
481 ],
482 ],
483 [ // graph
484 'A' => [
485 'A' => false,
486 'B' => true,
487 'C' => true,
488 'D' => true,
489 'E' => false,
490 'F' => false,
491 ],
492 'B' => [
493 'A' => false,
494 'B' => false,
495 'C' => false,
496 'D' => false,
497 'E' => false,
498 'F' => false,
499 ],
500 'C' => [
501 'A' => false,
502 'B' => false,
503 'C' => false,
504 'D' => false,
505 'E' => true,
506 'F' => false,
507 ],
508 'D' => [
509 'A' => false,
510 'B' => false,
511 'C' => false,
512 'D' => false,
513 'E' => true,
514 'F' => false,
515 ],
516 'E' => [
517 'A' => false,
518 'B' => false,
519 'C' => false,
520 'D' => false,
521 'E' => false,
522 'F' => false,
523 ],
524 'F' => [
525 'A' => false,
526 'B' => false,
527 'C' => false,
528 'D' => false,
529 'E' => false,
530 'F' => false,
531 ],
532 ],
533 ],
534 'Suggestions without reverse dependency' => [
535 [ // dependencies
536 'A' => [
537 'before' => [],
538 'after' => [],
539 'after-resilient' => ['B'] // package suggestion
540 ],
541 'B' => [
542 'before' => [],
543 'after' => [],
544 ],
545 'C' => [
546 'before' => [],
547 'after' => ['A']
548 ],
549 ],
550 [ // graph
551 'A' => [
552 'A' => false,
553 'B' => true,
554 'C' => false,
555 ],
556 'B' => [
557 'A' => false,
558 'B' => false,
559 'C' => false,
560 ],
561 'C' => [
562 'A' => true,
563 'B' => false,
564 'C' => false,
565 ],
566 ],
567 ],
568 'Suggestions with reverse dependency' => [
569 [ // dependencies
570 'A' => [
571 'before' => [],
572 'after' => [],
573 'after-resilient' => ['B'], // package suggestion
574 ],
575 'B' => [
576 'before' => [],
577 'after' => ['A']
578 ],
579 'C' => [
580 'before' => [],
581 'after' => ['A']
582 ],
583 ],
584 [ // graph
585 'A' => [
586 'A' => false,
587 'B' => false,
588 'C' => false,
589 ],
590 'B' => [
591 'A' => true,
592 'B' => false,
593 'C' => false,
594 ],
595 'C' => [
596 'A' => true,
597 'B' => false,
598 'C' => false,
599 ],
600 ],
601 ],
602 ];
603 }
604
605 /**
606 * @test
607 * @dataProvider calculateOrderResolvesCorrectOrderDataProvider
608 * @param array $graph
609 * @param array $expectedList
610 */
611 public function calculateOrderResolvesCorrectOrder(array $graph, array $expectedList)
612 {
613 $list = (new DependencyOrderingService())->calculateOrder($graph);
614 $this->assertSame($expectedList, $list);
615 }
616
617 /**
618 * @return array
619 */
620 public function calculateOrderResolvesCorrectOrderDataProvider()
621 {
622 return [
623 'list1' => [
624 [ // $graph
625 1 => [
626 1 => false,
627 2 => true
628 ],
629 2 => [
630 1 => false,
631 2 => false
632 ],
633 ],
634 [ // $expectedList
635 2, 1
636 ]
637 ],
638 'list2' => [
639 [ // $graph
640 1 => [
641 1 => false,
642 2 => true,
643 3 => false,
644 ],
645 2 => [
646 1 => false,
647 2 => false,
648 3 => false,
649 ],
650 3 => [
651 1 => true,
652 2 => true,
653 3 => false,
654 ],
655 ],
656 [ // $expectedList
657 2, 1, 3
658 ]
659 ],
660 ];
661 }
662
663 /**
664 * @test
665 */
666 public function calculateOrderDetectsCyclicGraph()
667 {
668 $this->expectException(\UnexpectedValueException::class);
669 $this->expectExceptionCode(1381960494);
670
671 (new DependencyOrderingService())->calculateOrder([
672 1 => [
673 1 => false,
674 2 => true,
675 ],
676 2 => [
677 1 => true,
678 2 => false,
679 ],
680 ]);
681 }
682
683 /**
684 * @return array
685 */
686 public function findPathInGraphReturnsCorrectPathDataProvider()
687 {
688 return [
689 'Simple path' => [
690 [
691 'A' => ['A' => false, 'B' => false, 'C' => false, 'Z' => true],
692 'B' => ['A' => false, 'B' => false, 'C' => false, 'Z' => false],
693 'C' => ['A' => false, 'B' => false, 'C' => false, 'Z' => false],
694 'Z' => ['A' => false, 'B' => false, 'C' => false, 'Z' => false]
695 ],
696 'A', 'Z',
697 ['A', 'Z']
698 ],
699 'No path' => [
700 [
701 'A' => ['A' => false, 'B' => true, 'C' => false, 'Z' => false],
702 'B' => ['A' => false, 'B' => false, 'C' => false, 'Z' => false],
703 'C' => ['A' => false, 'B' => true, 'C' => false, 'Z' => false],
704 'Z' => ['A' => false, 'B' => true, 'C' => false, 'Z' => false]
705 ],
706 'A', 'C',
707 []
708 ],
709 'Longer path' => [
710 [
711 'A' => ['A' => false, 'B' => true, 'C' => true, 'Z' => true],
712 'B' => ['A' => false, 'B' => false, 'C' => false, 'Z' => false],
713 'C' => ['A' => false, 'B' => false, 'C' => false, 'Z' => true],
714 'Z' => ['A' => false, 'B' => false, 'C' => false, 'Z' => false]
715 ],
716 'A', 'Z',
717 ['A', 'C', 'Z']
718 ],
719 ];
720 }
721
722 /**
723 * @param array $graph
724 * @param string $from
725 * @param string $to
726 * @param array $expected
727 * @test
728 * @dataProvider findPathInGraphReturnsCorrectPathDataProvider
729 */
730 public function findPathInGraphReturnsCorrectPath(array $graph, $from, $to, array $expected)
731 {
732 /** @var DependencyOrderingService|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $dependencyOrderingService */
733 $dependencyOrderingService = $this->getAccessibleMock(DependencyOrderingService::class, ['dummy']);
734 $path = $dependencyOrderingService->_call('findPathInGraph', $graph, $from, $to);
735
736 $this->assertSame($expected, $path);
737 }
738 }