[BUGFIX] Harden fallback class map generation
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Core / ClassLoadingInformationGeneratorTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Core;
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 Composer\Autoload\ClassLoader;
18 use TYPO3\CMS\Core\Core\ClassLoadingInformationGenerator;
19 use TYPO3\CMS\Core\Package\PackageInterface;
20 use TYPO3\CMS\Core\Tests\UnitTestCase;
21
22 /**
23 * Testcase for the ClassLoadingInformationGenerator class
24 */
25 class ClassLoadingInformationGeneratorTest extends UnitTestCase
26 {
27 /**
28 * @test
29 * @expectedException \TYPO3\CMS\Core\Error\Exception
30 * @expectedExceptionCode 1444142481
31 */
32 public function buildClassAliasMapForPackageThrowsExceptionForWrongComposerManifestInformation()
33 {
34 $packageMock = $this->createPackageMock([
35 'extra' => [
36 'typo3/class-alias-loader' => [
37 'class-alias-maps' => [
38 'foo' => Fixtures\test_extension\Resources\PHP\Test::class,
39 'bar' => Fixtures\test_extension\Resources\PHP\AnotherTestFile::class,
40 ],
41 ],
42 ],
43 ]);
44 /** @var ClassLoader|\PHPUnit_Framework_MockObject_MockObject $classLoaderMock */
45 $classLoaderMock = $this->getMock(ClassLoader::class);
46 $generator = new ClassLoadingInformationGenerator($classLoaderMock, [], __DIR__);
47 $generator->buildClassAliasMapForPackage($packageMock);
48 }
49
50 /**
51 * @test
52 * @expectedException \TYPO3\CMS\Core\Error\Exception
53 * @expectedExceptionCode 1422625075
54 */
55 public function buildClassAliasMapForPackageThrowsExceptionForWrongClassAliasMapFile()
56 {
57 $packageMock = $this->createPackageMock([
58 'extra' => [
59 'typo3/class-alias-loader' => [
60 'class-alias-maps' => [
61 'Migrations/Code/WrongClassAliasMap.php',
62 ],
63 ],
64 ],
65 ]);
66 /** @var ClassLoader|\PHPUnit_Framework_MockObject_MockObject $classLoaderMock */
67 $classLoaderMock = $this->getMock(ClassLoader::class);
68 $generator = new ClassLoadingInformationGenerator($classLoaderMock, [], __DIR__);
69 $generator->buildClassAliasMapForPackage($packageMock);
70 }
71
72 /**
73 * @test
74 */
75 public function buildClassAliasMapForPackageReturnsClassAliasMapForClassAliasMapFile()
76 {
77 $expectedClassMap = [
78 'aliasToClassNameMapping' => [
79 'foo' => Fixtures\test_extension\Resources\PHP\Test::class,
80 'bar' => Fixtures\test_extension\Resources\PHP\AnotherTestFile::class,
81 ],
82 'classNameToAliasMapping' => [
83 Fixtures\test_extension\Resources\PHP\Test::class => [
84 'foo' => 'foo',
85 ],
86 Fixtures\test_extension\Resources\PHP\AnotherTestFile::class => [
87 'bar' => 'bar',
88 ]
89 ],
90 ];
91 $packageMock = $this->createPackageMock(array());
92 /** @var ClassLoader|\PHPUnit_Framework_MockObject_MockObject $classLoaderMock */
93 $classLoaderMock = $this->getMock(ClassLoader::class);
94 $generator = new ClassLoadingInformationGenerator($classLoaderMock, [], __DIR__);
95 $this->assertEquals($expectedClassMap, $generator->buildClassAliasMapForPackage($packageMock));
96 }
97
98 /**
99 * @test
100 */
101 public function buildClassAliasMapForPackageReturnsClassAliasMapForComposerManifestInformation()
102 {
103 $expectedClassMap = [
104 'aliasToClassNameMapping' => [
105 'foo' => Fixtures\test_extension\Resources\PHP\Test::class,
106 'bar' => Fixtures\test_extension\Resources\PHP\AnotherTestFile::class,
107 ],
108 'classNameToAliasMapping' => [
109 Fixtures\test_extension\Resources\PHP\Test::class => [
110 'foo' => 'foo',
111 ],
112 Fixtures\test_extension\Resources\PHP\AnotherTestFile::class => [
113 'bar' => 'bar',
114 ]
115 ],
116 ];
117 $packageMock = $this->createPackageMock([
118 'extra' => [
119 'typo3/class-alias-loader' => [
120 'class-alias-maps' => [
121 'Resources/PHP/ClassAliasMap.php',
122 ],
123 ],
124 ],
125 ]);
126 /** @var ClassLoader|\PHPUnit_Framework_MockObject_MockObject $classLoaderMock */
127 $classLoaderMock = $this->getMock(ClassLoader::class);
128 $generator = new ClassLoadingInformationGenerator($classLoaderMock, [], __DIR__);
129 $this->assertEquals($expectedClassMap, $generator->buildClassAliasMapForPackage($packageMock));
130 }
131
132 /**
133 * Data provider for different autoload information
134 *
135 * @return array
136 */
137 public function autoloadFilesAreBuildCorrectlyDataProvider()
138 {
139 return [
140 'Psr-4 section' => [
141 [
142 'autoload' => [
143 'psr-4' => [
144 'TYPO3\\CMS\\TestExtension\\' => 'Classes/',
145 ],
146 ],
147 ],
148 [
149 '\'TYPO3\\\\CMS\\\\TestExtension\\\\\' => array($typo3InstallDir . \'/Fixtures/test_extension/Classes\')',
150 ],
151 [],
152 ],
153 'Psr-4 section without trailing slash' => [
154 [
155 'autoload' => [
156 'psr-4' => [
157 'TYPO3\\CMS\\TestExtension\\' => 'Classes',
158 ],
159 ],
160 ],
161 [
162 '\'TYPO3\\\\CMS\\\\TestExtension\\\\\' => array($typo3InstallDir . \'/Fixtures/test_extension/Classes\')',
163 ],
164 [],
165 ],
166 'Classmap section' => [
167 [
168 'autoload' => [
169 'classmap' => [
170 'Resources/PHP/',
171 ],
172 ],
173 ],
174 [],
175 [
176 '$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Test.php\'',
177 '$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/AnotherTestFile.php\'',
178 '$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Subdirectory/SubdirectoryTest.php\'',
179 ],
180 ],
181 'Classmap section without trailing slash' => [
182 [
183 'autoload' => [
184 'classmap' => [
185 'Resources/PHP',
186 ],
187 ],
188 ],
189 [],
190 [
191 '$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Test.php\'',
192 '$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/AnotherTestFile.php\'',
193 '$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Subdirectory/SubdirectoryTest.php\'',
194 ],
195 ],
196 'Classmap section pointing to a file' => [
197 [
198 'autoload' => [
199 'classmap' => [
200 'Resources/PHP/Test.php',
201 ],
202 ],
203 ],
204 [],
205 [
206 '$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Test.php\'',
207 '!$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/AnotherTestFile.php\'',
208 '!$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Subdirectory/SubdirectoryTest.php\'',
209 ],
210 ],
211 'No autoload section' => [
212 [],
213 [],
214 [
215 '!$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Test.php\'',
216 '!$typo3InstallDir . \'/Fixtures/test_extension/Tests/TestClass.php\'',
217 '!$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/AnotherTestFile.php\'',
218 '!$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Subdirectory/SubdirectoryTest.php\'',
219 '!$typo3InstallDir . \'/Fixtures/test_extension/class.ext_update.php\'',
220 ],
221 ],
222 'Classmap section pointing to two files' => [
223 [
224 'autoload' => [
225 'classmap' => [
226 'Resources/PHP/Test.php',
227 'Resources/PHP/AnotherTestFile.php',
228 ],
229 ],
230 ],
231 [],
232 [
233 '$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Test.php\'',
234 '$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/AnotherTestFile.php\'',
235 '!$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Subdirectory/SubdirectoryTest.php\'',
236 ],
237 ],
238 ];
239 }
240
241 /**
242 * @test
243 * @dataProvider autoloadFilesAreBuildCorrectlyDataProvider
244 *
245 * @param string $packageManifest
246 * @param array $expectedPsr4Files
247 * @param array $expectedClassMapFiles
248 */
249 public function autoloadFilesAreBuildCorrectly($packageManifest, $expectedPsr4Files, $expectedClassMapFiles)
250 {
251 /** @var ClassLoader|\PHPUnit_Framework_MockObject_MockObject $classLoaderMock */
252 $classLoaderMock = $this->getMock(ClassLoader::class);
253 $generator = new ClassLoadingInformationGenerator($classLoaderMock, [$this->createPackageMock($packageManifest)], __DIR__);
254 $files = $generator->buildAutoloadInformationFiles();
255
256 $this->assertArrayHasKey('psr-4File', $files);
257 $this->assertArrayHasKey('classMapFile', $files);
258 foreach ($expectedPsr4Files as $expectation) {
259 if ($expectation[0] === '!') {
260 $expectedCount = 0;
261 $expectation = substr($expectation, 1);
262 $message = sprintf('File "%s" is NOT expected to be in psr-4, but is.', $expectation);
263 } else {
264 $expectedCount = 1;
265 $message = sprintf('File "%s" is expected to be in psr-4, but is not.', $expectation);
266 }
267 $this->assertSame($expectedCount, substr_count($files['psr-4File'], $expectation), $message);
268 }
269 foreach ($expectedClassMapFiles as $expectation) {
270 if ($expectation[0] === '!') {
271 $expectedCount = 0;
272 $expectation = substr($expectation, 1);
273 $message = sprintf('File "%s" is NOT expected to be in class map, but is.', $expectation);
274 } else {
275 $expectedCount = 1;
276 $message = sprintf('File "%s" is expected to be in class map, but is not.', $expectation);
277 }
278 $this->assertSame($expectedCount, substr_count($files['classMapFile'], $expectation), $message);
279 }
280 }
281
282 /**
283 * Data provider for different autoload information
284 *
285 * @return array
286 */
287 public function autoloadDevFilesAreBuildCorrectlyDataProvider()
288 {
289 return [
290 'Psr-4 sections' => [
291 [
292 'autoload' => [
293 'psr-4' => [
294 'TYPO3\\CMS\\TestExtension\\' => 'Classes',
295 ],
296 ],
297 'autoload-dev' => [
298 'psr-4' => [
299 'TYPO3\\CMS\\TestExtension\\Tests\\' => 'Tests',
300 ],
301 ],
302 ],
303 [
304 '\'TYPO3\\\\CMS\\\\TestExtension\\\\\' => array($typo3InstallDir . \'/Fixtures/test_extension/Classes\')',
305 '\'TYPO3\\\\CMS\\\\TestExtension\\\\Tests\\\\\' => array($typo3InstallDir . \'/Fixtures/test_extension/Tests\')',
306 ],
307 [],
308 ],
309 'Psr-4 sections with override' => [
310 [
311 'autoload' => [
312 'psr-4' => [
313 'TYPO3\\CMS\\TestExtension\\' => 'Classes',
314 ],
315 ],
316 'autoload-dev' => [
317 'psr-4' => [
318 'TYPO3\\CMS\\TestExtension\\' => 'Tests',
319 ],
320 ],
321 ],
322 [
323 '!\'TYPO3\\\\CMS\\\\TestExtension\\\\\' => array($typo3InstallDir . \'/Fixtures/test_extension/Classes\')',
324 '\'TYPO3\\\\CMS\\\\TestExtension\\\\\' => array($typo3InstallDir . \'/Fixtures/test_extension/Tests\')',
325 ],
326 [],
327 ],
328 'Classmap section pointing to two files, one in dev and one not' => [
329 [
330 'autoload' => [
331 'classmap' => [
332 'Resources/PHP/Test.php',
333 ],
334 ],
335 'autoload-dev' => [
336 'classmap' => [
337 'Resources/PHP/AnotherTestFile.php',
338 ],
339 ],
340 ],
341 [],
342 [
343 '$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Test.php\'',
344 '$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/AnotherTestFile.php\'',
345 '!$typo3InstallDir . \'/Fixtures/test_extension/Resources/PHP/Subdirectory/SubdirectoryTest.php\'',
346 ],
347 ],
348 ];
349 }
350
351 /**
352 * @test
353 * @dataProvider autoloadDevFilesAreBuildCorrectlyDataProvider
354 *
355 * @param array $packageManifest
356 * @param array $expectedPsr4Files
357 * @param array $expectedClassMapFiles
358 */
359 public function autoloadDevFilesAreBuildCorrectly($packageManifest, $expectedPsr4Files, $expectedClassMapFiles)
360 {
361 /** @var ClassLoader|\PHPUnit_Framework_MockObject_MockObject $classLoaderMock */
362 $classLoaderMock = $this->getMock(ClassLoader::class);
363 $generator = new ClassLoadingInformationGenerator($classLoaderMock, [$this->createPackageMock($packageManifest)], __DIR__, true);
364 $files = $generator->buildAutoloadInformationFiles();
365
366 $this->assertArrayHasKey('psr-4File', $files);
367 $this->assertArrayHasKey('classMapFile', $files);
368 foreach ($expectedPsr4Files as $expectation) {
369 if ($expectation[0] === '!') {
370 $expectedCount = 0;
371 } else {
372 $expectedCount = 1;
373 }
374 $this->assertSame($expectedCount, substr_count($files['psr-4File'], $expectation), '' . $expectation);
375 }
376 foreach ($expectedClassMapFiles as $expectation) {
377 if ($expectation[0] === '!') {
378 $expectedCount = 0;
379 } else {
380 $expectedCount = 1;
381 }
382 $this->assertSame($expectedCount, substr_count($files['classMapFile'], $expectation), '' . $expectation);
383 }
384 }
385
386 /**
387 * @param array Array which should be returned as composer manifest
388 * @return PackageInterface
389 */
390 protected function createPackageMock($packageManifest)
391 {
392 $packageMock = $this->getMock(PackageInterface::class);
393 $packageMock->expects($this->any())->method('getPackagePath')->willReturn(__DIR__ . '/Fixtures/test_extension/');
394 $packageMock->expects($this->any())->method('getValueFromComposerManifest')->willReturn(
395 json_decode(json_encode($packageManifest))
396 );
397
398 return $packageMock;
399 }
400 }