1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace PackageVersionsTest; |
||||
6 | |||||
7 | use Composer\Composer; |
||||
8 | use Composer\Config; |
||||
9 | use Composer\EventDispatcher\EventDispatcher; |
||||
10 | use Composer\Installer\InstallationManager; |
||||
11 | use Composer\IO\IOInterface; |
||||
12 | use Composer\Package\Link; |
||||
13 | use Composer\Package\Locker; |
||||
14 | use Composer\Package\RootAliasPackage; |
||||
15 | use Composer\Package\RootPackage; |
||||
16 | use Composer\Package\RootPackageInterface; |
||||
17 | use Composer\Repository\InstalledRepositoryInterface; |
||||
18 | use Composer\Repository\RepositoryManager; |
||||
19 | use Composer\Script\Event; |
||||
20 | use PackageVersions\Installer; |
||||
21 | use PHPUnit\Framework\Exception; |
||||
22 | use PHPUnit\Framework\MockObject\MockObject; |
||||
23 | use PHPUnit\Framework\TestCase; |
||||
24 | use ReflectionClass; |
||||
25 | use RuntimeException; |
||||
26 | use function array_filter; |
||||
27 | use function array_map; |
||||
28 | use function chmod; |
||||
29 | use function file_get_contents; |
||||
30 | use function file_put_contents; |
||||
31 | use function fileperms; |
||||
32 | use function in_array; |
||||
33 | use function is_dir; |
||||
34 | use function mkdir; |
||||
35 | use function preg_match_all; |
||||
36 | use function realpath; |
||||
37 | use function rmdir; |
||||
38 | use function scandir; |
||||
39 | use function sprintf; |
||||
40 | use function strpos; |
||||
41 | use function substr; |
||||
42 | use function sys_get_temp_dir; |
||||
43 | use function uniqid; |
||||
44 | use function unlink; |
||||
45 | use const PHP_OS; |
||||
46 | |||||
47 | /** |
||||
48 | * @covers \PackageVersions\Installer |
||||
49 | */ |
||||
50 | final class InstallerTest extends TestCase |
||||
51 | { |
||||
52 | /** @var Composer&MockObject */ |
||||
53 | private $composer; |
||||
54 | |||||
55 | /** @var EventDispatcher&MockObject */ |
||||
56 | private $eventDispatcher; |
||||
57 | |||||
58 | /** @var IOInterface&MockObject */ |
||||
59 | private $io; |
||||
60 | |||||
61 | private Installer $installer; |
||||
62 | |||||
63 | /** |
||||
64 | * {@inheritDoc} |
||||
65 | * |
||||
66 | * @throws Exception |
||||
67 | */ |
||||
68 | protected function setUp() : void |
||||
69 | { |
||||
70 | parent::setUp(); |
||||
71 | |||||
72 | $this->installer = new Installer(); |
||||
73 | $this->io = $this->createMock(IOInterface::class); |
||||
74 | $this->composer = $this->createMock(Composer::class); |
||||
75 | $this->eventDispatcher = $this->createMock(EventDispatcher::class); |
||||
76 | |||||
77 | $this->composer->expects(self::any())->method('getEventDispatcher')->willReturn($this->eventDispatcher); |
||||
78 | } |
||||
79 | |||||
80 | public function testGetSubscribedEvents() : void |
||||
81 | { |
||||
82 | $events = Installer::getSubscribedEvents(); |
||||
83 | |||||
84 | self::assertSame( |
||||
85 | ['post-autoload-dump' => 'dumpVersionsClass'], |
||||
86 | $events |
||||
87 | ); |
||||
88 | |||||
89 | foreach ($events as $callback) { |
||||
90 | self::assertIsCallable([$this->installer, $callback]); |
||||
91 | } |
||||
92 | } |
||||
93 | |||||
94 | public function testDumpVersionsClassIfExistingFileIsNotWritable() : void |
||||
95 | { |
||||
96 | $config = $this->createMock(Config::class); |
||||
97 | $locker = $this->createMock(Locker::class); |
||||
98 | $repositoryManager = $this->createMock(RepositoryManager::class); |
||||
99 | $installManager = $this->createMock(InstallationManager::class); |
||||
100 | $repository = $this->createMock(InstalledRepositoryInterface::class); |
||||
101 | |||||
102 | $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true); |
||||
103 | |||||
104 | $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions'; |
||||
105 | |||||
106 | /** @noinspection MkdirRaceConditionInspection */ |
||||
107 | mkdir($expectedPath, 0777, true); |
||||
108 | |||||
109 | $expectedFileName = $expectedPath . '/Versions.php'; |
||||
110 | file_put_contents($expectedFileName, 'NOT PHP!'); |
||||
111 | chmod($expectedFileName, 0444); |
||||
112 | |||||
113 | $locker |
||||
114 | ->method('getLockData') |
||||
115 | ->willReturn([ |
||||
116 | 'packages' => [ |
||||
117 | [ |
||||
118 | 'name' => 'ocramius/package-versions', |
||||
119 | 'version' => '1.0.0', |
||||
120 | ], |
||||
121 | ], |
||||
122 | ]); |
||||
123 | |||||
124 | $repositoryManager->method('getLocalRepository')->willReturn($repository); |
||||
125 | |||||
126 | $this->composer->method('getConfig')->willReturn($config); |
||||
0 ignored issues
–
show
|
|||||
127 | $this->composer->method('getLocker')->willReturn($locker); |
||||
128 | $this->composer->method('getRepositoryManager')->willReturn($repositoryManager); |
||||
129 | $this->composer->method('getPackage')->willReturn($this->getRootPackageMock()); |
||||
130 | $this->composer->method('getInstallationManager')->willReturn($installManager); |
||||
131 | |||||
132 | $config->method('get')->with('vendor-dir')->willReturn($vendorDir); |
||||
133 | |||||
134 | Installer::dumpVersionsClass(new Event( |
||||
135 | 'post-install-cmd', |
||||
136 | $this->composer, |
||||
137 | $this->io |
||||
138 | )); |
||||
139 | |||||
140 | self::assertStringStartsWith('<?php', file_get_contents($expectedFileName)); |
||||
141 | |||||
142 | $this->rmDir($vendorDir); |
||||
143 | } |
||||
144 | |||||
145 | public function testDumpVersionsClassIfReadonlyFilesystem() : void |
||||
146 | { |
||||
147 | $config = $this->createMock(Config::class); |
||||
148 | $locker = $this->createMock(Locker::class); |
||||
149 | $repositoryManager = $this->createMock(RepositoryManager::class); |
||||
150 | $installManager = $this->createMock(InstallationManager::class); |
||||
151 | $repository = $this->createMock(InstalledRepositoryInterface::class); |
||||
152 | |||||
153 | $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true); |
||||
154 | |||||
155 | $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions'; |
||||
156 | |||||
157 | /** @noinspection MkdirRaceConditionInspection */ |
||||
158 | mkdir($expectedPath, 0700, true); |
||||
159 | |||||
160 | $expectedFileName = $expectedPath . '/Versions.php'; |
||||
161 | file_put_contents($expectedFileName, 'NOT PHP!'); |
||||
162 | chmod($expectedFileName, 0400); |
||||
163 | chmod($expectedPath, 0400); |
||||
164 | |||||
165 | $locker |
||||
166 | ->method('getLockData') |
||||
167 | ->willReturn([ |
||||
168 | 'packages' => [ |
||||
169 | [ |
||||
170 | 'name' => 'ocramius/package-versions', |
||||
171 | 'version' => '1.0.0', |
||||
172 | ], |
||||
173 | ], |
||||
174 | ]); |
||||
175 | |||||
176 | $repositoryManager->method('getLocalRepository')->willReturn($repository); |
||||
177 | |||||
178 | $this->composer->method('getConfig')->willReturn($config); |
||||
179 | $this->composer->method('getLocker')->willReturn($locker); |
||||
180 | $this->composer->method('getRepositoryManager')->willReturn($repositoryManager); |
||||
181 | $this->composer->method('getPackage')->willReturn($this->getRootPackageMock()); |
||||
182 | $this->composer->method('getInstallationManager')->willReturn($installManager); |
||||
183 | |||||
184 | $config->method('get')->with('vendor-dir')->willReturn($vendorDir); |
||||
185 | |||||
186 | Installer::dumpVersionsClass(new Event( |
||||
187 | 'post-install-cmd', |
||||
188 | $this->composer, |
||||
189 | $this->io |
||||
190 | )); |
||||
191 | |||||
192 | chmod($expectedPath, 0700); |
||||
193 | chmod($expectedFileName, 0600); |
||||
194 | |||||
195 | self::assertSame('NOT PHP!', file_get_contents($expectedFileName)); |
||||
196 | |||||
197 | $this->rmDir($vendorDir); |
||||
198 | } |
||||
199 | |||||
200 | public function testDumpVersionsClass() : void |
||||
201 | { |
||||
202 | $config = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock(); |
||||
203 | $locker = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock(); |
||||
204 | $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock(); |
||||
205 | $installManager = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock(); |
||||
206 | $repository = $this->createMock(InstalledRepositoryInterface::class); |
||||
207 | |||||
208 | $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true); |
||||
209 | |||||
210 | $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions'; |
||||
211 | |||||
212 | /** @noinspection MkdirRaceConditionInspection */ |
||||
213 | mkdir($expectedPath, 0777, true); |
||||
214 | |||||
215 | $locker |
||||
216 | ->expects(self::any()) |
||||
217 | ->method('getLockData') |
||||
218 | ->willReturn([ |
||||
219 | 'packages' => [ |
||||
220 | [ |
||||
221 | 'name' => 'ocramius/package-versions', |
||||
222 | 'version' => '1.0.0', |
||||
223 | ], |
||||
224 | [ |
||||
225 | 'name' => 'foo/bar', |
||||
226 | 'version' => '1.2.3', |
||||
227 | 'source' => ['reference' => 'abc123'], |
||||
228 | ], |
||||
229 | [ |
||||
230 | 'name' => 'baz/tab', |
||||
231 | 'version' => '4.5.6', |
||||
232 | 'source' => ['reference' => 'def456'], |
||||
233 | ], |
||||
234 | ], |
||||
235 | 'packages-dev' => [ |
||||
236 | [ |
||||
237 | 'name' => 'tar/taz', |
||||
238 | 'version' => '7.8.9', |
||||
239 | 'source' => ['reference' => 'ghi789'], |
||||
240 | ], |
||||
241 | ], |
||||
242 | ]); |
||||
243 | |||||
244 | $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository); |
||||
245 | |||||
246 | $this->composer->expects(self::any())->method('getConfig')->willReturn($config); |
||||
247 | $this->composer->expects(self::any())->method('getLocker')->willReturn($locker); |
||||
248 | $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager); |
||||
249 | $this->composer->expects(self::any())->method('getPackage')->willReturn($this->getRootPackageMock()); |
||||
250 | $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager); |
||||
251 | |||||
252 | $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir); |
||||
253 | |||||
254 | Installer::dumpVersionsClass(new Event( |
||||
255 | 'post-install-cmd', |
||||
256 | $this->composer, |
||||
257 | $this->io |
||||
258 | )); |
||||
259 | |||||
260 | $expectedSource = <<<'PHP' |
||||
261 | <?php |
||||
262 | |||||
263 | declare(strict_types=1); |
||||
264 | |||||
265 | namespace PackageVersions; |
||||
266 | |||||
267 | use OutOfBoundsException; |
||||
268 | |||||
269 | /** |
||||
270 | * This class is generated by ocramius/package-versions, specifically by |
||||
271 | * @see \PackageVersions\Installer |
||||
272 | * |
||||
273 | * This file is overwritten at every run of `composer install` or `composer update`. |
||||
274 | */ |
||||
275 | final class Versions |
||||
276 | { |
||||
277 | public const ROOT_PACKAGE_NAME = 'root/package'; |
||||
278 | /** |
||||
279 | * Array of all available composer packages. |
||||
280 | * Dont read this array from your calling code, but use the \PackageVersions\Versions::getVersion() method instead. |
||||
281 | * |
||||
282 | * @var array<string, string> |
||||
283 | * @internal |
||||
284 | */ |
||||
285 | public const VERSIONS = array ( |
||||
286 | 'ocramius/package-versions' => '1.0.0@', |
||||
287 | 'foo/bar' => '1.2.3@abc123', |
||||
288 | 'baz/tab' => '4.5.6@def456', |
||||
289 | 'tar/taz' => '7.8.9@ghi789', |
||||
290 | 'some-replaced/package' => '1.3.5@aaabbbcccddd', |
||||
291 | 'root/package' => '1.3.5@aaabbbcccddd', |
||||
292 | ); |
||||
293 | |||||
294 | private function __construct() |
||||
295 | { |
||||
296 | } |
||||
297 | |||||
298 | /** |
||||
299 | * @throws OutOfBoundsException If a version cannot be located. |
||||
300 | * |
||||
301 | * @psalm-param key-of<self::VERSIONS> $packageName |
||||
302 | * @psalm-pure |
||||
303 | */ |
||||
304 | public static function getVersion(string $packageName) : string |
||||
305 | { |
||||
306 | if (isset(self::VERSIONS[$packageName])) { |
||||
307 | return self::VERSIONS[$packageName]; |
||||
308 | } |
||||
309 | |||||
310 | throw new OutOfBoundsException( |
||||
311 | 'Required package "' . $packageName . '" is not installed: check your ./vendor/composer/installed.json and/or ./composer.lock files' |
||||
312 | ); |
||||
313 | } |
||||
314 | } |
||||
315 | |||||
316 | PHP; |
||||
317 | |||||
318 | self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php')); |
||||
319 | |||||
320 | $this->rmDir($vendorDir); |
||||
321 | } |
||||
322 | |||||
323 | public function testDumpVersionsClassNoDev() : void |
||||
324 | { |
||||
325 | $config = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock(); |
||||
326 | $locker = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock(); |
||||
327 | $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock(); |
||||
328 | $installManager = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock(); |
||||
329 | $repository = $this->createMock(InstalledRepositoryInterface::class); |
||||
330 | |||||
331 | $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true); |
||||
332 | |||||
333 | $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions'; |
||||
334 | |||||
335 | /** @noinspection MkdirRaceConditionInspection */ |
||||
336 | mkdir($expectedPath, 0777, true); |
||||
337 | |||||
338 | $locker |
||||
339 | ->expects(self::any()) |
||||
340 | ->method('getLockData') |
||||
341 | ->willReturn([ |
||||
342 | 'packages' => [ |
||||
343 | [ |
||||
344 | 'name' => 'ocramius/package-versions', |
||||
345 | 'version' => '1.0.0', |
||||
346 | ], |
||||
347 | [ |
||||
348 | 'name' => 'foo/bar', |
||||
349 | 'version' => '1.2.3', |
||||
350 | 'source' => ['reference' => 'abc123'], |
||||
351 | ], |
||||
352 | [ |
||||
353 | 'name' => 'baz/tab', |
||||
354 | 'version' => '4.5.6', |
||||
355 | 'source' => ['reference' => 'def456'], |
||||
356 | ], |
||||
357 | ], |
||||
358 | ]); |
||||
359 | |||||
360 | $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository); |
||||
361 | |||||
362 | $this->composer->expects(self::any())->method('getConfig')->willReturn($config); |
||||
363 | $this->composer->expects(self::any())->method('getLocker')->willReturn($locker); |
||||
364 | $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager); |
||||
365 | $this->composer->expects(self::any())->method('getPackage')->willReturn($this->getRootPackageMock()); |
||||
366 | $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager); |
||||
367 | |||||
368 | $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir); |
||||
369 | |||||
370 | Installer::dumpVersionsClass(new Event( |
||||
371 | 'post-install-cmd', |
||||
372 | $this->composer, |
||||
373 | $this->io |
||||
374 | )); |
||||
375 | |||||
376 | $expectedSource = <<<'PHP' |
||||
377 | <?php |
||||
378 | |||||
379 | declare(strict_types=1); |
||||
380 | |||||
381 | namespace PackageVersions; |
||||
382 | |||||
383 | use OutOfBoundsException; |
||||
384 | |||||
385 | /** |
||||
386 | * This class is generated by ocramius/package-versions, specifically by |
||||
387 | * @see \PackageVersions\Installer |
||||
388 | * |
||||
389 | * This file is overwritten at every run of `composer install` or `composer update`. |
||||
390 | */ |
||||
391 | final class Versions |
||||
392 | { |
||||
393 | public const ROOT_PACKAGE_NAME = 'root/package'; |
||||
394 | /** |
||||
395 | * Array of all available composer packages. |
||||
396 | * Dont read this array from your calling code, but use the \PackageVersions\Versions::getVersion() method instead. |
||||
397 | * |
||||
398 | * @var array<string, string> |
||||
399 | * @internal |
||||
400 | */ |
||||
401 | public const VERSIONS = array ( |
||||
402 | 'ocramius/package-versions' => '1.0.0@', |
||||
403 | 'foo/bar' => '1.2.3@abc123', |
||||
404 | 'baz/tab' => '4.5.6@def456', |
||||
405 | 'some-replaced/package' => '1.3.5@aaabbbcccddd', |
||||
406 | 'root/package' => '1.3.5@aaabbbcccddd', |
||||
407 | ); |
||||
408 | |||||
409 | private function __construct() |
||||
410 | { |
||||
411 | } |
||||
412 | |||||
413 | /** |
||||
414 | * @throws OutOfBoundsException If a version cannot be located. |
||||
415 | * |
||||
416 | * @psalm-param key-of<self::VERSIONS> $packageName |
||||
417 | * @psalm-pure |
||||
418 | */ |
||||
419 | public static function getVersion(string $packageName) : string |
||||
420 | { |
||||
421 | if (isset(self::VERSIONS[$packageName])) { |
||||
422 | return self::VERSIONS[$packageName]; |
||||
423 | } |
||||
424 | |||||
425 | throw new OutOfBoundsException( |
||||
426 | 'Required package "' . $packageName . '" is not installed: check your ./vendor/composer/installed.json and/or ./composer.lock files' |
||||
427 | ); |
||||
428 | } |
||||
429 | } |
||||
430 | |||||
431 | PHP; |
||||
432 | |||||
433 | self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php')); |
||||
434 | |||||
435 | $this->rmDir($vendorDir); |
||||
436 | } |
||||
437 | |||||
438 | /** |
||||
439 | * @throws RuntimeException |
||||
440 | * |
||||
441 | * @group #12 |
||||
442 | */ |
||||
443 | public function testDumpVersionsWithoutPackageSourceDetails() : void |
||||
444 | { |
||||
445 | $config = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock(); |
||||
446 | $locker = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock(); |
||||
447 | $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock(); |
||||
448 | $installManager = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock(); |
||||
449 | $repository = $this->createMock(InstalledRepositoryInterface::class); |
||||
450 | |||||
451 | $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true); |
||||
452 | |||||
453 | $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions'; |
||||
454 | |||||
455 | /** @noinspection MkdirRaceConditionInspection */ |
||||
456 | mkdir($expectedPath, 0777, true); |
||||
457 | |||||
458 | $locker |
||||
459 | ->expects(self::any()) |
||||
460 | ->method('getLockData') |
||||
461 | ->willReturn([ |
||||
462 | 'packages' => [ |
||||
463 | [ |
||||
464 | 'name' => 'ocramius/package-versions', |
||||
465 | 'version' => '1.0.0', |
||||
466 | ], |
||||
467 | [ |
||||
468 | 'name' => 'foo/bar', |
||||
469 | 'version' => '1.2.3', |
||||
470 | 'dist' => ['reference' => 'abc123'], // version defined in the dist, this time |
||||
471 | ], |
||||
472 | [ |
||||
473 | 'name' => 'baz/tab', |
||||
474 | 'version' => '4.5.6', // source missing |
||||
475 | ], |
||||
476 | ], |
||||
477 | ]); |
||||
478 | |||||
479 | $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository); |
||||
480 | |||||
481 | $this->composer->expects(self::any())->method('getConfig')->willReturn($config); |
||||
482 | $this->composer->expects(self::any())->method('getLocker')->willReturn($locker); |
||||
483 | $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager); |
||||
484 | $this->composer->expects(self::any())->method('getPackage')->willReturn($this->getRootPackageMock()); |
||||
485 | $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager); |
||||
486 | |||||
487 | $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir); |
||||
488 | |||||
489 | Installer::dumpVersionsClass(new Event( |
||||
490 | 'post-install-cmd', |
||||
491 | $this->composer, |
||||
492 | $this->io |
||||
493 | )); |
||||
494 | |||||
495 | $expectedSource = <<<'PHP' |
||||
496 | <?php |
||||
497 | |||||
498 | declare(strict_types=1); |
||||
499 | |||||
500 | namespace PackageVersions; |
||||
501 | |||||
502 | use OutOfBoundsException; |
||||
503 | |||||
504 | /** |
||||
505 | * This class is generated by ocramius/package-versions, specifically by |
||||
506 | * @see \PackageVersions\Installer |
||||
507 | * |
||||
508 | * This file is overwritten at every run of `composer install` or `composer update`. |
||||
509 | */ |
||||
510 | final class Versions |
||||
511 | { |
||||
512 | public const ROOT_PACKAGE_NAME = 'root/package'; |
||||
513 | /** |
||||
514 | * Array of all available composer packages. |
||||
515 | * Dont read this array from your calling code, but use the \PackageVersions\Versions::getVersion() method instead. |
||||
516 | * |
||||
517 | * @var array<string, string> |
||||
518 | * @internal |
||||
519 | */ |
||||
520 | public const VERSIONS = array ( |
||||
521 | 'ocramius/package-versions' => '1.0.0@', |
||||
522 | 'foo/bar' => '1.2.3@abc123', |
||||
523 | 'baz/tab' => '4.5.6@', |
||||
524 | 'some-replaced/package' => '1.3.5@aaabbbcccddd', |
||||
525 | 'root/package' => '1.3.5@aaabbbcccddd', |
||||
526 | ); |
||||
527 | |||||
528 | private function __construct() |
||||
529 | { |
||||
530 | } |
||||
531 | |||||
532 | /** |
||||
533 | * @throws OutOfBoundsException If a version cannot be located. |
||||
534 | * |
||||
535 | * @psalm-param key-of<self::VERSIONS> $packageName |
||||
536 | * @psalm-pure |
||||
537 | */ |
||||
538 | public static function getVersion(string $packageName) : string |
||||
539 | { |
||||
540 | if (isset(self::VERSIONS[$packageName])) { |
||||
541 | return self::VERSIONS[$packageName]; |
||||
542 | } |
||||
543 | |||||
544 | throw new OutOfBoundsException( |
||||
545 | 'Required package "' . $packageName . '" is not installed: check your ./vendor/composer/installed.json and/or ./composer.lock files' |
||||
546 | ); |
||||
547 | } |
||||
548 | } |
||||
549 | |||||
550 | PHP; |
||||
551 | |||||
552 | self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php')); |
||||
553 | |||||
554 | $this->rmDir($vendorDir); |
||||
555 | } |
||||
556 | |||||
557 | /** |
||||
558 | * @throws RuntimeException |
||||
559 | * |
||||
560 | * @dataProvider rootPackageProvider |
||||
561 | */ |
||||
562 | public function testDumpsVersionsClassToSpecificLocation(RootPackageInterface $rootPackage, bool $inVendor) : void |
||||
563 | { |
||||
564 | $config = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock(); |
||||
565 | $locker = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock(); |
||||
566 | $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock(); |
||||
567 | $installManager = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock(); |
||||
568 | $repository = $this->createMock(InstalledRepositoryInterface::class); |
||||
569 | |||||
570 | $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true) . '/vendor'; |
||||
571 | |||||
572 | /** @noinspection MkdirRaceConditionInspection */ |
||||
573 | mkdir($vendorDir, 0777, true); |
||||
574 | |||||
575 | /** @noinspection RealpathInSteamContextInspection */ |
||||
576 | $expectedPath = $inVendor |
||||
577 | ? $vendorDir . '/ocramius/package-versions/src/PackageVersions' |
||||
578 | : realpath($vendorDir . '/..') . '/src/PackageVersions'; |
||||
579 | |||||
580 | /** @noinspection MkdirRaceConditionInspection */ |
||||
581 | mkdir($expectedPath, 0777, true); |
||||
582 | |||||
583 | $locker |
||||
584 | ->expects(self::any()) |
||||
585 | ->method('getLockData') |
||||
586 | ->willReturn([ |
||||
587 | 'packages' => [ |
||||
588 | [ |
||||
589 | 'name' => 'ocramius/package-versions', |
||||
590 | 'version' => '1.0.0', |
||||
591 | ], |
||||
592 | ], |
||||
593 | 'packages-dev' => [], |
||||
594 | ]); |
||||
595 | |||||
596 | $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository); |
||||
597 | |||||
598 | $this->composer->expects(self::any())->method('getConfig')->willReturn($config); |
||||
599 | $this->composer->expects(self::any())->method('getLocker')->willReturn($locker); |
||||
600 | $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager); |
||||
601 | $this->composer->expects(self::any())->method('getPackage')->willReturn($rootPackage); |
||||
602 | $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager); |
||||
603 | |||||
604 | $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir); |
||||
605 | |||||
606 | Installer::dumpVersionsClass(new Event( |
||||
607 | 'post-install-cmd', |
||||
608 | $this->composer, |
||||
609 | $this->io |
||||
610 | )); |
||||
611 | |||||
612 | self::assertStringMatchesFormat( |
||||
613 | '%Aclass Versions%A1.2.3@%A', |
||||
614 | file_get_contents($expectedPath . '/Versions.php') |
||||
615 | ); |
||||
616 | |||||
617 | $this->rmDir($vendorDir); |
||||
618 | } |
||||
619 | |||||
620 | /** |
||||
621 | * @return bool[][]|RootPackageInterface[][] the root package and whether the versions class is to be generated in |
||||
622 | * the vendor dir or not |
||||
623 | */ |
||||
624 | public function rootPackageProvider() : array |
||||
625 | { |
||||
626 | $baseRootPackage = new RootPackage('root/package', '1.2.3', '1.2.3'); |
||||
627 | $aliasRootPackage = new RootAliasPackage($baseRootPackage, '1.2.3', '1.2.3'); |
||||
628 | $indirectAliasRootPackage = new RootAliasPackage($aliasRootPackage, '1.2.3', '1.2.3'); |
||||
629 | $packageVersionsRootPackage = new RootPackage('ocramius/package-versions', '1.2.3', '1.2.3'); |
||||
630 | $aliasPackageVersionsRootPackage = new RootAliasPackage($packageVersionsRootPackage, '1.2.3', '1.2.3'); |
||||
631 | $indirectAliasPackageVersionsRootPackage = new RootAliasPackage( |
||||
632 | $aliasPackageVersionsRootPackage, |
||||
633 | '1.2.3', |
||||
634 | '1.2.3' |
||||
635 | ); |
||||
636 | |||||
637 | return [ |
||||
638 | 'root package is not ocramius/package-versions' => [ |
||||
639 | $baseRootPackage, |
||||
640 | true, |
||||
641 | ], |
||||
642 | 'alias root package is not ocramius/package-versions' => [ |
||||
643 | $aliasRootPackage, |
||||
644 | true, |
||||
645 | ], |
||||
646 | 'indirect alias root package is not ocramius/package-versions' => [ |
||||
647 | $indirectAliasRootPackage, |
||||
648 | true, |
||||
649 | ], |
||||
650 | 'root package is ocramius/package-versions' => [ |
||||
651 | $packageVersionsRootPackage, |
||||
652 | false, |
||||
653 | ], |
||||
654 | 'alias root package is ocramius/package-versions' => [ |
||||
655 | $aliasPackageVersionsRootPackage, |
||||
656 | false, |
||||
657 | ], |
||||
658 | 'indirect alias root package is ocramius/package-versions' => [ |
||||
659 | $indirectAliasPackageVersionsRootPackage, |
||||
660 | false, |
||||
661 | ], |
||||
662 | ]; |
||||
663 | } |
||||
664 | |||||
665 | public function testVersionsAreNotDumpedIfPackageVersionsNotExplicitlyRequired() : void |
||||
666 | { |
||||
667 | $config = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock(); |
||||
668 | $locker = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock(); |
||||
669 | $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock(); |
||||
670 | $installManager = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock(); |
||||
671 | $repository = $this->createMock(InstalledRepositoryInterface::class); |
||||
672 | $package = $this->createMock(RootPackageInterface::class); |
||||
673 | |||||
674 | $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true); |
||||
675 | |||||
676 | $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions'; |
||||
677 | |||||
678 | /** @noinspection MkdirRaceConditionInspection */ |
||||
679 | mkdir($expectedPath, 0777, true); |
||||
680 | |||||
681 | $locker |
||||
682 | ->expects(self::any()) |
||||
683 | ->method('getLockData') |
||||
684 | ->willReturn([ |
||||
685 | 'packages' => [ |
||||
686 | [ |
||||
687 | 'name' => 'foo/bar', |
||||
688 | 'version' => '1.2.3', |
||||
689 | 'dist' => ['reference' => 'abc123'], // version defined in the dist, this time |
||||
690 | ], |
||||
691 | [ |
||||
692 | 'name' => 'baz/tab', |
||||
693 | 'version' => '4.5.6', // source missing |
||||
694 | ], |
||||
695 | ], |
||||
696 | ]); |
||||
697 | |||||
698 | $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository); |
||||
699 | |||||
700 | $this->composer->expects(self::any())->method('getConfig')->willReturn($config); |
||||
701 | $this->composer->expects(self::any())->method('getLocker')->willReturn($locker); |
||||
702 | $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager); |
||||
703 | $this->composer->expects(self::any())->method('getPackage')->willReturn($package); |
||||
704 | $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager); |
||||
705 | |||||
706 | $package->expects(self::any())->method('getName')->willReturn('root/package'); |
||||
707 | $package->expects(self::any())->method('getVersion')->willReturn('1.3.5'); |
||||
708 | $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd'); |
||||
709 | $package->expects(self::any())->method('getReplaces')->willReturn([]); |
||||
710 | |||||
711 | $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir); |
||||
712 | |||||
713 | Installer::dumpVersionsClass(new Event( |
||||
714 | 'post-install-cmd', |
||||
715 | $this->composer, |
||||
716 | $this->io |
||||
717 | )); |
||||
718 | |||||
719 | self::assertFileDoesNotExist($expectedPath . '/Versions.php'); |
||||
720 | |||||
721 | $this->rmDir($vendorDir); |
||||
722 | } |
||||
723 | |||||
724 | /** |
||||
725 | * @group #41 |
||||
726 | * @group #46 |
||||
727 | */ |
||||
728 | public function testVersionsAreNotDumpedIfPackageIsScheduledForRemoval() : void |
||||
729 | { |
||||
730 | $config = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock(); |
||||
731 | $locker = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock(); |
||||
732 | $package = $this->createMock(RootPackageInterface::class); |
||||
733 | $package->expects(self::any())->method('getReplaces')->willReturn([]); |
||||
734 | $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true); |
||||
735 | $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions'; |
||||
736 | |||||
737 | $locker |
||||
738 | ->expects(self::any()) |
||||
739 | ->method('getLockData') |
||||
740 | ->willReturn([ |
||||
741 | 'packages' => [ |
||||
742 | [ |
||||
743 | 'name' => 'ocramius/package-versions', |
||||
744 | 'version' => '1.0.0', |
||||
745 | ], |
||||
746 | ], |
||||
747 | ]); |
||||
748 | |||||
749 | $package->expects(self::any())->method('getName')->willReturn('root/package'); |
||||
750 | |||||
751 | $this->composer->expects(self::any())->method('getConfig')->willReturn($config); |
||||
752 | $this->composer->expects(self::any())->method('getLocker')->willReturn($locker); |
||||
753 | $this->composer->expects(self::any())->method('getPackage')->willReturn($package); |
||||
754 | |||||
755 | $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir); |
||||
756 | |||||
757 | Installer::dumpVersionsClass(new Event( |
||||
758 | 'post-install-cmd', |
||||
759 | $this->composer, |
||||
760 | $this->io |
||||
761 | )); |
||||
762 | |||||
763 | self::assertFileDoesNotExist($expectedPath . '/Versions.php'); |
||||
764 | self::assertFileDoesNotExist($expectedPath . '/Versions.php'); |
||||
765 | } |
||||
766 | |||||
767 | public function testGeneratedVersionFileAccessRights() : void |
||||
768 | { |
||||
769 | if (strpos(PHP_OS, 'WIN') === 0) { |
||||
770 | $this->markTestSkipped('Windows is kinda "meh" at file access levels'); |
||||
771 | } |
||||
772 | |||||
773 | $config = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock(); |
||||
774 | $locker = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock(); |
||||
775 | $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock(); |
||||
776 | $installManager = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock(); |
||||
777 | $repository = $this->createMock(InstalledRepositoryInterface::class); |
||||
778 | $package = $this->createMock(RootPackageInterface::class); |
||||
779 | $package->expects(self::any())->method('getReplaces')->willReturn([]); |
||||
780 | |||||
781 | $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true); |
||||
782 | |||||
783 | $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions'; |
||||
784 | |||||
785 | /** @noinspection MkdirRaceConditionInspection */ |
||||
786 | mkdir($expectedPath, 0777, true); |
||||
787 | |||||
788 | $locker |
||||
789 | ->expects(self::any()) |
||||
790 | ->method('getLockData') |
||||
791 | ->willReturn([ |
||||
792 | 'packages' => [ |
||||
793 | [ |
||||
794 | 'name' => 'ocramius/package-versions', |
||||
795 | 'version' => '1.0.0', |
||||
796 | ], |
||||
797 | ], |
||||
798 | ]); |
||||
799 | |||||
800 | $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository); |
||||
801 | |||||
802 | $this->composer->expects(self::any())->method('getConfig')->willReturn($config); |
||||
803 | $this->composer->expects(self::any())->method('getLocker')->willReturn($locker); |
||||
804 | $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager); |
||||
805 | $this->composer->expects(self::any())->method('getPackage')->willReturn($package); |
||||
806 | $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager); |
||||
807 | |||||
808 | $package->expects(self::any())->method('getName')->willReturn('root/package'); |
||||
809 | $package->expects(self::any())->method('getVersion')->willReturn('1.3.5'); |
||||
810 | $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd'); |
||||
811 | |||||
812 | $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir); |
||||
813 | |||||
814 | Installer::dumpVersionsClass(new Event( |
||||
815 | 'post-install-cmd', |
||||
816 | $this->composer, |
||||
817 | $this->io |
||||
818 | )); |
||||
819 | |||||
820 | $filePath = $expectedPath . '/Versions.php'; |
||||
821 | |||||
822 | self::assertFileExists($filePath); |
||||
823 | self::assertSame('0664', substr(sprintf('%o', fileperms($filePath)), -4)); |
||||
824 | |||||
825 | $this->rmDir($vendorDir); |
||||
826 | } |
||||
827 | |||||
828 | private function rmDir(string $directory) : void |
||||
829 | { |
||||
830 | if (! is_dir($directory)) { |
||||
831 | unlink($directory); |
||||
832 | |||||
833 | return; |
||||
834 | } |
||||
835 | |||||
836 | array_map( |
||||
837 | function ($item) use ($directory) : void { |
||||
838 | $this->rmDir($directory . '/' . $item); |
||||
839 | }, |
||||
840 | array_filter( |
||||
841 | scandir($directory), |
||||
0 ignored issues
–
show
It seems like
scandir($directory) can also be of type false ; however, parameter $input of array_filter() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
842 | static function (string $dirItem) { |
||||
843 | return ! in_array($dirItem, ['.', '..'], true); |
||||
844 | } |
||||
845 | ) |
||||
846 | ); |
||||
847 | |||||
848 | rmdir($directory); |
||||
849 | } |
||||
850 | |||||
851 | /** |
||||
852 | * @group composer/composer#5237 |
||||
853 | */ |
||||
854 | public function testWillEscapeRegexParsingOfClassDefinitions() : void |
||||
855 | { |
||||
856 | self::assertSame( |
||||
857 | 1, |
||||
858 | preg_match_all( |
||||
859 | '{^((?:final\s+)?(?:\s*))class\s+(\S+)}mi', |
||||
860 | file_get_contents((new ReflectionClass(Installer::class))->getFileName()) |
||||
861 | ) |
||||
862 | ); |
||||
863 | } |
||||
864 | |||||
865 | public function testGetVersionsIsNotNormalizedForRootPackage() : void |
||||
866 | { |
||||
867 | $config = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock(); |
||||
868 | $locker = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock(); |
||||
869 | $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock(); |
||||
870 | $installManager = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock(); |
||||
871 | $repository = $this->createMock(InstalledRepositoryInterface::class); |
||||
872 | |||||
873 | $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true); |
||||
874 | |||||
875 | $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions'; |
||||
876 | |||||
877 | /** @noinspection MkdirRaceConditionInspection */ |
||||
878 | mkdir($expectedPath, 0777, true); |
||||
879 | |||||
880 | $locker |
||||
881 | ->expects(self::any()) |
||||
882 | ->method('getLockData') |
||||
883 | ->willReturn([ |
||||
884 | 'packages' => [ |
||||
885 | [ |
||||
886 | 'name' => 'ocramius/package-versions', |
||||
887 | 'version' => '1.0.0', |
||||
888 | ], |
||||
889 | ], |
||||
890 | ]); |
||||
891 | |||||
892 | $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository); |
||||
893 | |||||
894 | $this->composer->expects(self::any())->method('getConfig')->willReturn($config); |
||||
895 | $this->composer->expects(self::any())->method('getLocker')->willReturn($locker); |
||||
896 | $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager); |
||||
897 | $this->composer->expects(self::any())->method('getPackage')->willReturn($this->getRootPackageMock()); |
||||
898 | $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager); |
||||
899 | |||||
900 | $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir); |
||||
901 | |||||
902 | Installer::dumpVersionsClass(new Event( |
||||
903 | 'post-install-cmd', |
||||
904 | $this->composer, |
||||
905 | $this->io |
||||
906 | )); |
||||
907 | |||||
908 | $expectedSource = <<<'PHP' |
||||
909 | <?php |
||||
910 | |||||
911 | declare(strict_types=1); |
||||
912 | |||||
913 | namespace PackageVersions; |
||||
914 | |||||
915 | use OutOfBoundsException; |
||||
916 | |||||
917 | /** |
||||
918 | * This class is generated by ocramius/package-versions, specifically by |
||||
919 | * @see \PackageVersions\Installer |
||||
920 | * |
||||
921 | * This file is overwritten at every run of `composer install` or `composer update`. |
||||
922 | */ |
||||
923 | final class Versions |
||||
924 | { |
||||
925 | public const ROOT_PACKAGE_NAME = 'root/package'; |
||||
926 | /** |
||||
927 | * Array of all available composer packages. |
||||
928 | * Dont read this array from your calling code, but use the \PackageVersions\Versions::getVersion() method instead. |
||||
929 | * |
||||
930 | * @var array<string, string> |
||||
931 | * @internal |
||||
932 | */ |
||||
933 | public const VERSIONS = array ( |
||||
934 | 'ocramius/package-versions' => '1.0.0@', |
||||
935 | 'some-replaced/package' => '1.3.5@aaabbbcccddd', |
||||
936 | 'root/package' => '1.3.5@aaabbbcccddd', |
||||
937 | ); |
||||
938 | |||||
939 | private function __construct() |
||||
940 | { |
||||
941 | } |
||||
942 | |||||
943 | /** |
||||
944 | * @throws OutOfBoundsException If a version cannot be located. |
||||
945 | * |
||||
946 | * @psalm-param key-of<self::VERSIONS> $packageName |
||||
947 | * @psalm-pure |
||||
948 | */ |
||||
949 | public static function getVersion(string $packageName) : string |
||||
950 | { |
||||
951 | if (isset(self::VERSIONS[$packageName])) { |
||||
952 | return self::VERSIONS[$packageName]; |
||||
953 | } |
||||
954 | |||||
955 | throw new OutOfBoundsException( |
||||
956 | 'Required package "' . $packageName . '" is not installed: check your ./vendor/composer/installed.json and/or ./composer.lock files' |
||||
957 | ); |
||||
958 | } |
||||
959 | } |
||||
960 | |||||
961 | PHP; |
||||
962 | |||||
963 | self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php')); |
||||
964 | |||||
965 | $this->rmDir($vendorDir); |
||||
966 | } |
||||
967 | |||||
968 | private function getRootPackageMock() : RootPackageInterface |
||||
969 | { |
||||
970 | $package = $this->createMock(RootPackageInterface::class); |
||||
971 | $package->expects(self::any())->method('getName')->willReturn('root/package'); |
||||
972 | $package->expects(self::any())->method('getPrettyVersion')->willReturn('1.3.5'); |
||||
973 | $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd'); |
||||
974 | |||||
975 | $link = $this->createMock(Link::class); |
||||
976 | $link->expects(self::any())->method('getTarget')->willReturn('some-replaced/package'); |
||||
977 | $link->expects(self::any())->method('getPrettyConstraint')->willReturn('self.version'); |
||||
978 | |||||
979 | $package->expects(self::any())->method('getReplaces')->willReturn([$link]); |
||||
980 | |||||
981 | return $package; |
||||
0 ignored issues
–
show
|
|||||
982 | } |
||||
983 | } |
||||
984 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.