Passed
Pull Request — master (#146)
by Marco
06:14
created

InstallerTest::testDumpVersionsWithoutPackageSourceDetails()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 112
Code Lines 89

Duplication

Lines 0
Ratio 0 %

Importance

Changes 13
Bugs 0 Features 0
Metric Value
cc 1
eloc 89
c 13
b 0
f 0
nc 1
nop 0
dl 0
loc 112
rs 8.24

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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);
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 Composer\InstalledVersions;
268
use OutOfBoundsException;
269
270
/**
271
 * This class is generated by ocramius/package-versions, specifically by
272
 * @see \PackageVersions\Installer
273
 *
274
 * This file is overwritten at every run of `composer install` or `composer update`.
275
 */
276
final class Versions
277
{
278
    /**
279
     * @deprecated please use {@see \Composer\InstalledVersions::getRootPackage()} instead. The
280
     *             equivalent expression for this constant's contents is
281
     *             `\Composer\InstalledVersions::getRootPackage()['name']`.
282
     *             This constant will be removed in version 2.0.0.
283
     */
284
    public const ROOT_PACKAGE_NAME = 'root/package';
285
286
    private function __construct()
287
    {
288
    }
289
290
    /**
291
     * @throws OutOfBoundsException If a version cannot be located.
292
     *
293
     * @psalm-pure
294
     */
295
    public static function getVersion(string $packageName) : string
296
    {
297
        return InstalledVersions::getPrettyVersion($packageName)
298
            . '@' . InstalledVersions::getReference($packageName);
299
    }
300
}
301
302
PHP;
303
304
        self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php'));
305
306
        $this->rmDir($vendorDir);
307
    }
308
309
    public function testDumpVersionsClassNoDev() : void
310
    {
311
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
312
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
313
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
314
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
315
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
316
317
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
318
319
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
320
321
        /** @noinspection MkdirRaceConditionInspection */
322
        mkdir($expectedPath, 0777, true);
323
324
        $locker
325
            ->expects(self::any())
326
            ->method('getLockData')
327
            ->willReturn([
328
                'packages' => [
329
                    [
330
                        'name'    => 'ocramius/package-versions',
331
                        'version' => '1.0.0',
332
                    ],
333
                    [
334
                        'name'    => 'foo/bar',
335
                        'version' => '1.2.3',
336
                        'source'  => ['reference' => 'abc123'],
337
                    ],
338
                    [
339
                        'name'    => 'baz/tab',
340
                        'version' => '4.5.6',
341
                        'source'  => ['reference' => 'def456'],
342
                    ],
343
                ],
344
            ]);
345
346
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
347
348
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
349
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
350
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
351
        $this->composer->expects(self::any())->method('getPackage')->willReturn($this->getRootPackageMock());
352
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
353
354
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
355
356
        Installer::dumpVersionsClass(new Event(
357
            'post-install-cmd',
358
            $this->composer,
359
            $this->io
360
        ));
361
362
        $expectedSource = <<<'PHP'
363
<?php
364
365
declare(strict_types=1);
366
367
namespace PackageVersions;
368
369
use Composer\InstalledVersions;
370
use OutOfBoundsException;
371
372
/**
373
 * This class is generated by ocramius/package-versions, specifically by
374
 * @see \PackageVersions\Installer
375
 *
376
 * This file is overwritten at every run of `composer install` or `composer update`.
377
 */
378
final class Versions
379
{
380
    /**
381
     * @deprecated please use {@see \Composer\InstalledVersions::getRootPackage()} instead. The
382
     *             equivalent expression for this constant's contents is
383
     *             `\Composer\InstalledVersions::getRootPackage()['name']`.
384
     *             This constant will be removed in version 2.0.0.
385
     */
386
    public const ROOT_PACKAGE_NAME = 'root/package';
387
388
    private function __construct()
389
    {
390
    }
391
392
    /**
393
     * @throws OutOfBoundsException If a version cannot be located.
394
     *
395
     * @psalm-pure
396
     */
397
    public static function getVersion(string $packageName) : string
398
    {
399
        return InstalledVersions::getPrettyVersion($packageName)
400
            . '@' . InstalledVersions::getReference($packageName);
401
    }
402
}
403
404
PHP;
405
406
        self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php'));
407
408
        $this->rmDir($vendorDir);
409
    }
410
411
    /**
412
     * @throws RuntimeException
413
     *
414
     * @group #12
415
     */
416
    public function testDumpVersionsWithoutPackageSourceDetails() : void
417
    {
418
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
419
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
420
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
421
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
422
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
423
424
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
425
426
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
427
428
        /** @noinspection MkdirRaceConditionInspection */
429
        mkdir($expectedPath, 0777, true);
430
431
        $locker
432
            ->expects(self::any())
433
            ->method('getLockData')
434
            ->willReturn([
435
                'packages' => [
436
                    [
437
                        'name'    => 'ocramius/package-versions',
438
                        'version' => '1.0.0',
439
                    ],
440
                    [
441
                        'name'    => 'foo/bar',
442
                        'version' => '1.2.3',
443
                        'dist'  => ['reference' => 'abc123'], // version defined in the dist, this time
444
                    ],
445
                    [
446
                        'name'    => 'baz/tab',
447
                        'version' => '4.5.6', // source missing
448
                    ],
449
                ],
450
            ]);
451
452
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
453
454
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
455
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
456
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
457
        $this->composer->expects(self::any())->method('getPackage')->willReturn($this->getRootPackageMock());
458
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
459
460
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
461
462
        Installer::dumpVersionsClass(new Event(
463
            'post-install-cmd',
464
            $this->composer,
465
            $this->io
466
        ));
467
468
        $expectedSource = <<<'PHP'
469
<?php
470
471
declare(strict_types=1);
472
473
namespace PackageVersions;
474
475
use Composer\InstalledVersions;
476
use OutOfBoundsException;
477
478
/**
479
 * This class is generated by ocramius/package-versions, specifically by
480
 * @see \PackageVersions\Installer
481
 *
482
 * This file is overwritten at every run of `composer install` or `composer update`.
483
 */
484
final class Versions
485
{
486
    /**
487
     * @deprecated please use {@see \Composer\InstalledVersions::getRootPackage()} instead. The
488
     *             equivalent expression for this constant's contents is
489
     *             `\Composer\InstalledVersions::getRootPackage()['name']`.
490
     *             This constant will be removed in version 2.0.0.
491
     */
492
    public const ROOT_PACKAGE_NAME = 'root/package';
493
494
    private function __construct()
495
    {
496
    }
497
498
    /**
499
     * @throws OutOfBoundsException If a version cannot be located.
500
     *
501
     * @psalm-pure
502
     */
503
    public static function getVersion(string $packageName) : string
504
    {
505
        return InstalledVersions::getPrettyVersion($packageName)
506
            . '@' . InstalledVersions::getReference($packageName);
507
    }
508
}
509
510
PHP;
511
512
        self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php'));
513
514
        $this->rmDir($vendorDir);
515
    }
516
517
    /**
518
     * @throws RuntimeException
519
     *
520
     * @dataProvider rootPackageProvider
521
     */
522
    public function testDumpsVersionsClassToSpecificLocation(RootPackageInterface $rootPackage, bool $inVendor) : void
523
    {
524
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
525
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
526
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
527
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
528
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
529
530
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true) . '/vendor';
531
532
        /** @noinspection MkdirRaceConditionInspection */
533
        mkdir($vendorDir, 0777, true);
534
535
        /** @noinspection RealpathInSteamContextInspection */
536
        $expectedPath = $inVendor
537
            ? $vendorDir . '/ocramius/package-versions/src/PackageVersions'
538
            : realpath($vendorDir . '/..') . '/src/PackageVersions';
539
540
        /** @noinspection MkdirRaceConditionInspection */
541
        mkdir($expectedPath, 0777, true);
542
543
        $locker
544
            ->expects(self::any())
545
            ->method('getLockData')
546
            ->willReturn([
547
                'packages' => [
548
                    [
549
                        'name'    => 'ocramius/package-versions',
550
                        'version' => '1.0.0',
551
                    ],
552
                ],
553
                'packages-dev' => [],
554
            ]);
555
556
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
557
558
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
559
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
560
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
561
        $this->composer->expects(self::any())->method('getPackage')->willReturn($rootPackage);
562
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
563
564
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
565
566
        Installer::dumpVersionsClass(new Event(
567
            'post-install-cmd',
568
            $this->composer,
569
            $this->io
570
        ));
571
572
        $generatedSource = file_get_contents($expectedPath . '/Versions.php');
573
574
        self::assertStringNotContainsString(
575
            'unknown/root-package@UNKNOWN',
576
            $generatedSource
577
        );
578
579
        self::assertStringMatchesFormat(
580
            '%Aclass Versions%AROOT_PACKAGE_NAME%A',
581
            $generatedSource
582
        );
583
584
        $this->rmDir($vendorDir);
585
    }
586
587
    /**
588
     * @return bool[][]|RootPackageInterface[][] the root package and whether the versions class is to be generated in
589
     *                                           the vendor dir or not
590
     */
591
    public function rootPackageProvider() : array
592
    {
593
        $baseRootPackage                         = new RootPackage('root/package', '1.2.3', '1.2.3');
594
        $aliasRootPackage                        = new RootAliasPackage($baseRootPackage, '1.2.3', '1.2.3');
595
        $indirectAliasRootPackage                = new RootAliasPackage($aliasRootPackage, '1.2.3', '1.2.3');
596
        $packageVersionsRootPackage              = new RootPackage('ocramius/package-versions', '1.2.3', '1.2.3');
597
        $aliasPackageVersionsRootPackage         = new RootAliasPackage($packageVersionsRootPackage, '1.2.3', '1.2.3');
598
        $indirectAliasPackageVersionsRootPackage = new RootAliasPackage(
599
            $aliasPackageVersionsRootPackage,
600
            '1.2.3',
601
            '1.2.3'
602
        );
603
604
        return [
605
            'root package is not ocramius/package-versions' => [
606
                $baseRootPackage,
607
                true,
608
            ],
609
            'alias root package is not ocramius/package-versions' => [
610
                $aliasRootPackage,
611
                true,
612
            ],
613
            'indirect alias root package is not ocramius/package-versions' => [
614
                $indirectAliasRootPackage,
615
                true,
616
            ],
617
            'root package is ocramius/package-versions' => [
618
                $packageVersionsRootPackage,
619
                false,
620
            ],
621
            'alias root package is ocramius/package-versions' => [
622
                $aliasPackageVersionsRootPackage,
623
                false,
624
            ],
625
            'indirect alias root package is ocramius/package-versions' => [
626
                $indirectAliasPackageVersionsRootPackage,
627
                false,
628
            ],
629
        ];
630
    }
631
632
    public function testVersionsAreNotDumpedIfPackageVersionsNotExplicitlyRequired() : void
633
    {
634
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
635
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
636
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
637
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
638
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
639
        $package           = $this->createMock(RootPackageInterface::class);
640
641
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
642
643
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
644
645
        /** @noinspection MkdirRaceConditionInspection */
646
        mkdir($expectedPath, 0777, true);
647
648
        $locker
649
            ->expects(self::any())
650
            ->method('getLockData')
651
            ->willReturn([
652
                'packages' => [
653
                    [
654
                        'name'    => 'foo/bar',
655
                        'version' => '1.2.3',
656
                        'dist'  => ['reference' => 'abc123'], // version defined in the dist, this time
657
                    ],
658
                    [
659
                        'name'    => 'baz/tab',
660
                        'version' => '4.5.6', // source missing
661
                    ],
662
                ],
663
            ]);
664
665
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
666
667
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
668
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
669
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
670
        $this->composer->expects(self::any())->method('getPackage')->willReturn($package);
671
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
672
673
        $package->expects(self::any())->method('getName')->willReturn('root/package');
674
        $package->expects(self::any())->method('getVersion')->willReturn('1.3.5');
675
        $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd');
676
        $package->expects(self::any())->method('getReplaces')->willReturn([]);
677
678
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
679
680
        Installer::dumpVersionsClass(new Event(
681
            'post-install-cmd',
682
            $this->composer,
683
            $this->io
684
        ));
685
686
        self::assertFileDoesNotExist($expectedPath . '/Versions.php');
687
688
        $this->rmDir($vendorDir);
689
    }
690
691
    /**
692
     * @group #41
693
     * @group #46
694
     */
695
    public function testVersionsAreNotDumpedIfPackageIsScheduledForRemoval() : void
696
    {
697
        $config  = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
698
        $locker  = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
699
        $package = $this->createMock(RootPackageInterface::class);
700
        $package->expects(self::any())->method('getReplaces')->willReturn([]);
701
        $vendorDir    = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
702
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
703
704
        $locker
705
            ->expects(self::any())
706
            ->method('getLockData')
707
            ->willReturn([
708
                'packages' => [
709
                    [
710
                        'name'    => 'ocramius/package-versions',
711
                        'version' => '1.0.0',
712
                    ],
713
                ],
714
            ]);
715
716
        $package->expects(self::any())->method('getName')->willReturn('root/package');
717
718
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
719
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
720
        $this->composer->expects(self::any())->method('getPackage')->willReturn($package);
721
722
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
723
724
        Installer::dumpVersionsClass(new Event(
725
            'post-install-cmd',
726
            $this->composer,
727
            $this->io
728
        ));
729
730
        self::assertFileDoesNotExist($expectedPath . '/Versions.php');
731
        self::assertFileDoesNotExist($expectedPath . '/Versions.php');
732
    }
733
734
    public function testGeneratedVersionFileAccessRights() : void
735
    {
736
        if (strpos(PHP_OS, 'WIN') === 0) {
737
            $this->markTestSkipped('Windows is kinda "meh" at file access levels');
738
        }
739
740
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
741
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
742
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
743
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
744
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
745
        $package           = $this->createMock(RootPackageInterface::class);
746
        $package->expects(self::any())->method('getReplaces')->willReturn([]);
747
748
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
749
750
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
751
752
        /** @noinspection MkdirRaceConditionInspection */
753
        mkdir($expectedPath, 0777, true);
754
755
        $locker
756
            ->expects(self::any())
757
            ->method('getLockData')
758
            ->willReturn([
759
                'packages' => [
760
                    [
761
                        'name'    => 'ocramius/package-versions',
762
                        'version' => '1.0.0',
763
                    ],
764
                ],
765
            ]);
766
767
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
768
769
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
770
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
771
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
772
        $this->composer->expects(self::any())->method('getPackage')->willReturn($package);
773
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
774
775
        $package->expects(self::any())->method('getName')->willReturn('root/package');
776
        $package->expects(self::any())->method('getVersion')->willReturn('1.3.5');
777
        $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd');
778
779
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
780
781
        Installer::dumpVersionsClass(new Event(
782
            'post-install-cmd',
783
            $this->composer,
784
            $this->io
785
        ));
786
787
        $filePath = $expectedPath . '/Versions.php';
788
789
        self::assertFileExists($filePath);
790
        self::assertSame('0664', substr(sprintf('%o', fileperms($filePath)), -4));
791
792
        $this->rmDir($vendorDir);
793
    }
794
795
    private function rmDir(string $directory) : void
796
    {
797
        if (! is_dir($directory)) {
798
            unlink($directory);
799
800
            return;
801
        }
802
803
        array_map(
804
            function ($item) use ($directory) : void {
805
                $this->rmDir($directory . '/' . $item);
806
            },
807
            array_filter(
808
                scandir($directory),
809
                static function (string $dirItem) {
810
                    return ! in_array($dirItem, ['.', '..'], true);
811
                }
812
            )
813
        );
814
815
        rmdir($directory);
816
    }
817
818
    /**
819
     * @group composer/composer#5237
820
     */
821
    public function testWillEscapeRegexParsingOfClassDefinitions() : void
822
    {
823
        self::assertSame(
824
            1,
825
            preg_match_all(
826
                '{^((?:final\s+)?(?:\s*))class\s+(\S+)}mi',
827
                file_get_contents((new ReflectionClass(Installer::class))->getFileName())
828
            )
829
        );
830
    }
831
832
    public function testGetVersionsIsNotNormalizedForRootPackage() : void
833
    {
834
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
835
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
836
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
837
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
838
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
839
840
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
841
842
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
843
844
        /** @noinspection MkdirRaceConditionInspection */
845
        mkdir($expectedPath, 0777, true);
846
847
        $locker
848
            ->expects(self::any())
849
            ->method('getLockData')
850
            ->willReturn([
851
                'packages' => [
852
                    [
853
                        'name'    => 'ocramius/package-versions',
854
                        'version' => '1.0.0',
855
                    ],
856
                ],
857
            ]);
858
859
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
860
861
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
862
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
863
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
864
        $this->composer->expects(self::any())->method('getPackage')->willReturn($this->getRootPackageMock());
865
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
866
867
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
868
869
        Installer::dumpVersionsClass(new Event(
870
            'post-install-cmd',
871
            $this->composer,
872
            $this->io
873
        ));
874
875
        $expectedSource = <<<'PHP'
876
<?php
877
878
declare(strict_types=1);
879
880
namespace PackageVersions;
881
882
use Composer\InstalledVersions;
883
use OutOfBoundsException;
884
885
/**
886
 * This class is generated by ocramius/package-versions, specifically by
887
 * @see \PackageVersions\Installer
888
 *
889
 * This file is overwritten at every run of `composer install` or `composer update`.
890
 */
891
final class Versions
892
{
893
    /**
894
     * @deprecated please use {@see \Composer\InstalledVersions::getRootPackage()} instead. The
895
     *             equivalent expression for this constant's contents is
896
     *             `\Composer\InstalledVersions::getRootPackage()['name']`.
897
     *             This constant will be removed in version 2.0.0.
898
     */
899
    public const ROOT_PACKAGE_NAME = 'root/package';
900
901
    private function __construct()
902
    {
903
    }
904
905
    /**
906
     * @throws OutOfBoundsException If a version cannot be located.
907
     *
908
     * @psalm-pure
909
     */
910
    public static function getVersion(string $packageName) : string
911
    {
912
        return InstalledVersions::getPrettyVersion($packageName)
913
            . '@' . InstalledVersions::getReference($packageName);
914
    }
915
}
916
917
PHP;
918
919
        self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php'));
920
921
        $this->rmDir($vendorDir);
922
    }
923
924
    private function getRootPackageMock() : RootPackageInterface
925
    {
926
        $package = $this->createMock(RootPackageInterface::class);
927
        $package->expects(self::any())->method('getName')->willReturn('root/package');
928
        $package->expects(self::any())->method('getPrettyVersion')->willReturn('1.3.5');
929
        $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd');
930
931
        $link = $this->createMock(Link::class);
932
        $link->expects(self::any())->method('getTarget')->willReturn('some-replaced/package');
933
        $link->expects(self::any())->method('getPrettyConstraint')->willReturn('self.version');
934
935
        $package->expects(self::any())->method('getReplaces')->willReturn([$link]);
936
937
        return $package;
938
    }
939
}
940