Completed
Push — master ( e1c506...a4d4b6 )
by Marco
10s
created

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