Completed
Pull Request — master (#118)
by Marco
01:39
created

InstallerTest::testDumpVersionsClassIfExistingFileIsNotWritable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 50
rs 9.0909
c 0
b 0
f 0
cc 1
nc 1
nop 0
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;
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

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