Completed
Push — master ( e511a3...651c37 )
by Marco
21s queued 11s
created

)   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 7
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PackageVersionsTest;
6
7
use Composer\Composer;
8
use Composer\Config;
9
use Composer\EventDispatcher\EventDispatcher;
10
use Composer\Installer\InstallationManager;
11
use Composer\IO\IOInterface;
12
use Composer\Package\Link;
13
use Composer\Package\Locker;
14
use Composer\Package\RootAliasPackage;
15
use Composer\Package\RootPackage;
16
use Composer\Package\RootPackageInterface;
17
use Composer\Repository\InstalledRepositoryInterface;
18
use Composer\Repository\RepositoryManager;
19
use Composer\Script\Event;
20
use PackageVersions\Installer;
21
use PHPUnit\Framework\Exception;
22
use PHPUnit\Framework\MockObject\MockObject;
23
use PHPUnit\Framework\TestCase;
24
use ReflectionClass;
25
use RuntimeException;
26
use function array_filter;
27
use function array_map;
28
use function chmod;
29
use function file_get_contents;
30
use function file_put_contents;
31
use function fileperms;
32
use function in_array;
33
use function is_dir;
34
use function mkdir;
35
use function preg_match_all;
36
use function realpath;
37
use function rmdir;
38
use function scandir;
39
use function sprintf;
40
use function strpos;
41
use function substr;
42
use function sys_get_temp_dir;
43
use function uniqid;
44
use function unlink;
45
use const PHP_OS;
46
47
/**
48
 * @covers \PackageVersions\Installer
49
 */
50
final class InstallerTest extends TestCase
51
{
52
    /** @var Composer&MockObject */
53
    private $composer;
54
55
    /** @var EventDispatcher&MockObject */
56
    private $eventDispatcher;
57
58
    /** @var IOInterface&MockObject */
59
    private $io;
60
61
    private Installer $installer;
62
63
    /**
64
     * {@inheritDoc}
65
     *
66
     * @throws Exception
67
     */
68
    protected function setUp() : void
69
    {
70
        parent::setUp();
71
72
        $this->installer       = new Installer();
73
        $this->io              = $this->createMock(IOInterface::class);
74
        $this->composer        = $this->createMock(Composer::class);
75
        $this->eventDispatcher = $this->createMock(EventDispatcher::class);
76
77
        $this->composer->expects(self::any())->method('getEventDispatcher')->willReturn($this->eventDispatcher);
78
    }
79
80
    public function testGetSubscribedEvents() : void
81
    {
82
        $events = Installer::getSubscribedEvents();
83
84
        self::assertSame(
85
            ['post-autoload-dump' => 'dumpVersionsClass'],
86
            $events
87
        );
88
89
        foreach ($events as $callback) {
90
            self::assertIsCallable([$this->installer, $callback]);
91
        }
92
    }
93
94
    public function testDumpVersionsClassIfExistingFileIsNotWritable() : void
95
    {
96
        $config            = $this->createMock(Config::class);
97
        $locker            = $this->createMock(Locker::class);
98
        $repositoryManager = $this->createMock(RepositoryManager::class);
99
        $installManager    = $this->createMock(InstallationManager::class);
100
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
101
102
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
103
104
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
105
106
        /** @noinspection MkdirRaceConditionInspection */
107
        mkdir($expectedPath, 0777, true);
108
109
        $expectedFileName = $expectedPath . '/Versions.php';
110
        file_put_contents($expectedFileName, 'NOT PHP!');
111
        chmod($expectedFileName, 0444);
112
113
        $locker
114
            ->method('getLockData')
115
            ->willReturn([
116
                'packages' => [
117
                    [
118
                        'name'    => 'ocramius/package-versions',
119
                        'version' => '1.0.0',
120
                    ],
121
                ],
122
            ]);
123
124
        $repositoryManager->method('getLocalRepository')->willReturn($repository);
125
126
        $this->composer->method('getConfig')->willReturn($config);
0 ignored issues
show
Bug introduced by
The method method() does not exist on Composer\Composer. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

126
        $this->composer->/** @scrutinizer ignore-call */ 
127
                         method('getConfig')->willReturn($config);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
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);
0 ignored issues
show
Bug introduced by
The method rmDir() does not exist on PackageVersionsTest\InstallerTest. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

142
        $this->/** @scrutinizer ignore-call */ 
143
               rmDir($vendorDir);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

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