Completed
Pull Request — master (#79)
by
unknown
02:08
created

testDumpVersionsClassIfExistingFileIsNotWritable()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 115

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 115
rs 8
c 0
b 0
f 0
cc 1
nc 1
nop 0

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