InstallerTest::rmDir()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 11
c 2
b 0
f 0
nc 2
nop 1
dl 0
loc 21
rs 9.9
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);
143
    }
144
145
    public function testDumpVersionsClassIfReadonlyFilesystem() : void
146
    {
147
        $config            = $this->createMock(Config::class);
148
        $locker            = $this->createMock(Locker::class);
149
        $repositoryManager = $this->createMock(RepositoryManager::class);
150
        $installManager    = $this->createMock(InstallationManager::class);
151
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
152
153
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
154
155
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
156
157
        /** @noinspection MkdirRaceConditionInspection */
158
        mkdir($expectedPath, 0700, true);
159
160
        $expectedFileName = $expectedPath . '/Versions.php';
161
        file_put_contents($expectedFileName, 'NOT PHP!');
162
        chmod($expectedFileName, 0400);
163
        chmod($expectedPath, 0400);
164
165
        $locker
166
            ->method('getLockData')
167
            ->willReturn([
168
                'packages' => [
169
                    [
170
                        'name'    => 'ocramius/package-versions',
171
                        'version' => '1.0.0',
172
                    ],
173
                ],
174
            ]);
175
176
        $repositoryManager->method('getLocalRepository')->willReturn($repository);
177
178
        $this->composer->method('getConfig')->willReturn($config);
179
        $this->composer->method('getLocker')->willReturn($locker);
180
        $this->composer->method('getRepositoryManager')->willReturn($repositoryManager);
181
        $this->composer->method('getPackage')->willReturn($this->getRootPackageMock());
182
        $this->composer->method('getInstallationManager')->willReturn($installManager);
183
184
        $config->method('get')->with('vendor-dir')->willReturn($vendorDir);
185
186
        Installer::dumpVersionsClass(new Event(
187
            'post-install-cmd',
188
            $this->composer,
189
            $this->io
190
        ));
191
192
        chmod($expectedPath, 0700);
193
        chmod($expectedFileName, 0600);
194
195
        self::assertSame('NOT PHP!', file_get_contents($expectedFileName));
196
197
        $this->rmDir($vendorDir);
198
    }
199
200
    public function testDumpVersionsClass() : void
201
    {
202
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
203
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
204
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
205
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
206
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
207
208
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
209
210
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
211
212
        /** @noinspection MkdirRaceConditionInspection */
213
        mkdir($expectedPath, 0777, true);
214
215
        $locker
216
            ->expects(self::any())
217
            ->method('getLockData')
218
            ->willReturn([
219
                'packages' => [
220
                    [
221
                        'name'    => 'ocramius/package-versions',
222
                        'version' => '1.0.0',
223
                    ],
224
                    [
225
                        'name'    => 'foo/bar',
226
                        'version' => '1.2.3',
227
                        'source'  => ['reference' => 'abc123'],
228
                    ],
229
                    [
230
                        'name'    => 'baz/tab',
231
                        'version' => '4.5.6',
232
                        'source'  => ['reference' => 'def456'],
233
                    ],
234
                ],
235
                'packages-dev' => [
236
                    [
237
                        'name'    => 'tar/taz',
238
                        'version' => '7.8.9',
239
                        'source'  => ['reference' => 'ghi789'],
240
                    ],
241
                ],
242
            ]);
243
244
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
245
246
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
247
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
248
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
249
        $this->composer->expects(self::any())->method('getPackage')->willReturn($this->getRootPackageMock());
250
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
251
252
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
253
254
        Installer::dumpVersionsClass(new Event(
255
            'post-install-cmd',
256
            $this->composer,
257
            $this->io
258
        ));
259
260
        $expectedSource = <<<'PHP'
261
<?php
262
263
declare(strict_types=1);
264
265
namespace PackageVersions;
266
267
use OutOfBoundsException;
268
269
/**
270
 * This class is generated by ocramius/package-versions, specifically by
271
 * @see \PackageVersions\Installer
272
 *
273
 * This file is overwritten at every run of `composer install` or `composer update`.
274
 */
275
final class Versions
276
{
277
    public const ROOT_PACKAGE_NAME = 'root/package';
278
    /**
279
     * Array of all available composer packages.
280
     * Dont read this array from your calling code, but use the \PackageVersions\Versions::getVersion() method instead.
281
     *
282
     * @var array<string, string>
283
     * @internal
284
     */
285
    public const VERSIONS          = array (
286
  'ocramius/package-versions' => '1.0.0@',
287
  'foo/bar' => '1.2.3@abc123',
288
  'baz/tab' => '4.5.6@def456',
289
  'tar/taz' => '7.8.9@ghi789',
290
  'some-replaced/package' => '1.3.5@aaabbbcccddd',
291
  'root/package' => '1.3.5@aaabbbcccddd',
292
);
293
294
    private function __construct()
295
    {
296
    }
297
298
    /**
299
     * @throws OutOfBoundsException If a version cannot be located.
300
     *
301
     * @psalm-param key-of<self::VERSIONS> $packageName
302
     * @psalm-pure
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: check your ./vendor/composer/installed.json and/or ./composer.lock files'
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
use OutOfBoundsException;
384
385
/**
386
 * This class is generated by ocramius/package-versions, specifically by
387
 * @see \PackageVersions\Installer
388
 *
389
 * This file is overwritten at every run of `composer install` or `composer update`.
390
 */
391
final class Versions
392
{
393
    public const ROOT_PACKAGE_NAME = 'root/package';
394
    /**
395
     * Array of all available composer packages.
396
     * Dont read this array from your calling code, but use the \PackageVersions\Versions::getVersion() method instead.
397
     *
398
     * @var array<string, string>
399
     * @internal
400
     */
401
    public const VERSIONS          = array (
402
  'ocramius/package-versions' => '1.0.0@',
403
  'foo/bar' => '1.2.3@abc123',
404
  'baz/tab' => '4.5.6@def456',
405
  'some-replaced/package' => '1.3.5@aaabbbcccddd',
406
  'root/package' => '1.3.5@aaabbbcccddd',
407
);
408
409
    private function __construct()
410
    {
411
    }
412
413
    /**
414
     * @throws OutOfBoundsException If a version cannot be located.
415
     *
416
     * @psalm-param key-of<self::VERSIONS> $packageName
417
     * @psalm-pure
418
     */
419
    public static function getVersion(string $packageName) : string
420
    {
421
        if (isset(self::VERSIONS[$packageName])) {
422
            return self::VERSIONS[$packageName];
423
        }
424
425
        throw new OutOfBoundsException(
426
            'Required package "' . $packageName . '" is not installed: check your ./vendor/composer/installed.json and/or ./composer.lock files'
427
        );
428
    }
429
}
430
431
PHP;
432
433
        self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php'));
434
435
        $this->rmDir($vendorDir);
436
    }
437
438
    /**
439
     * @throws RuntimeException
440
     *
441
     * @group #12
442
     */
443
    public function testDumpVersionsWithoutPackageSourceDetails() : void
444
    {
445
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
446
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
447
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
448
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
449
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
450
451
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
452
453
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
454
455
        /** @noinspection MkdirRaceConditionInspection */
456
        mkdir($expectedPath, 0777, true);
457
458
        $locker
459
            ->expects(self::any())
460
            ->method('getLockData')
461
            ->willReturn([
462
                'packages' => [
463
                    [
464
                        'name'    => 'ocramius/package-versions',
465
                        'version' => '1.0.0',
466
                    ],
467
                    [
468
                        'name'    => 'foo/bar',
469
                        'version' => '1.2.3',
470
                        'dist'  => ['reference' => 'abc123'], // version defined in the dist, this time
471
                    ],
472
                    [
473
                        'name'    => 'baz/tab',
474
                        'version' => '4.5.6', // source missing
475
                    ],
476
                ],
477
            ]);
478
479
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
480
481
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
482
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
483
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
484
        $this->composer->expects(self::any())->method('getPackage')->willReturn($this->getRootPackageMock());
485
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
486
487
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
488
489
        Installer::dumpVersionsClass(new Event(
490
            'post-install-cmd',
491
            $this->composer,
492
            $this->io
493
        ));
494
495
        $expectedSource = <<<'PHP'
496
<?php
497
498
declare(strict_types=1);
499
500
namespace PackageVersions;
501
502
use OutOfBoundsException;
503
504
/**
505
 * This class is generated by ocramius/package-versions, specifically by
506
 * @see \PackageVersions\Installer
507
 *
508
 * This file is overwritten at every run of `composer install` or `composer update`.
509
 */
510
final class Versions
511
{
512
    public const ROOT_PACKAGE_NAME = 'root/package';
513
    /**
514
     * Array of all available composer packages.
515
     * Dont read this array from your calling code, but use the \PackageVersions\Versions::getVersion() method instead.
516
     *
517
     * @var array<string, string>
518
     * @internal
519
     */
520
    public const VERSIONS          = array (
521
  'ocramius/package-versions' => '1.0.0@',
522
  'foo/bar' => '1.2.3@abc123',
523
  'baz/tab' => '4.5.6@',
524
  'some-replaced/package' => '1.3.5@aaabbbcccddd',
525
  'root/package' => '1.3.5@aaabbbcccddd',
526
);
527
528
    private function __construct()
529
    {
530
    }
531
532
    /**
533
     * @throws OutOfBoundsException If a version cannot be located.
534
     *
535
     * @psalm-param key-of<self::VERSIONS> $packageName
536
     * @psalm-pure
537
     */
538
    public static function getVersion(string $packageName) : string
539
    {
540
        if (isset(self::VERSIONS[$packageName])) {
541
            return self::VERSIONS[$packageName];
542
        }
543
544
        throw new OutOfBoundsException(
545
            'Required package "' . $packageName . '" is not installed: check your ./vendor/composer/installed.json and/or ./composer.lock files'
546
        );
547
    }
548
}
549
550
PHP;
551
552
        self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php'));
553
554
        $this->rmDir($vendorDir);
555
    }
556
557
    /**
558
     * @throws RuntimeException
559
     *
560
     * @dataProvider rootPackageProvider
561
     */
562
    public function testDumpsVersionsClassToSpecificLocation(RootPackageInterface $rootPackage, bool $inVendor) : void
563
    {
564
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
565
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
566
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
567
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
568
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
569
570
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true) . '/vendor';
571
572
        /** @noinspection MkdirRaceConditionInspection */
573
        mkdir($vendorDir, 0777, true);
574
575
        /** @noinspection RealpathInSteamContextInspection */
576
        $expectedPath = $inVendor
577
            ? $vendorDir . '/ocramius/package-versions/src/PackageVersions'
578
            : realpath($vendorDir . '/..') . '/src/PackageVersions';
579
580
        /** @noinspection MkdirRaceConditionInspection */
581
        mkdir($expectedPath, 0777, true);
582
583
        $locker
584
            ->expects(self::any())
585
            ->method('getLockData')
586
            ->willReturn([
587
                'packages' => [
588
                    [
589
                        'name'    => 'ocramius/package-versions',
590
                        'version' => '1.0.0',
591
                    ],
592
                ],
593
                'packages-dev' => [],
594
            ]);
595
596
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
597
598
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
599
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
600
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
601
        $this->composer->expects(self::any())->method('getPackage')->willReturn($rootPackage);
602
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
603
604
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
605
606
        Installer::dumpVersionsClass(new Event(
607
            'post-install-cmd',
608
            $this->composer,
609
            $this->io
610
        ));
611
612
        self::assertStringMatchesFormat(
613
            '%Aclass Versions%A1.2.3@%A',
614
            file_get_contents($expectedPath . '/Versions.php')
615
        );
616
617
        $this->rmDir($vendorDir);
618
    }
619
620
    /**
621
     * @return bool[][]|RootPackageInterface[][] the root package and whether the versions class is to be generated in
622
     *                                           the vendor dir or not
623
     */
624
    public function rootPackageProvider() : array
625
    {
626
        $baseRootPackage                         = new RootPackage('root/package', '1.2.3', '1.2.3');
627
        $aliasRootPackage                        = new RootAliasPackage($baseRootPackage, '1.2.3', '1.2.3');
628
        $indirectAliasRootPackage                = new RootAliasPackage($aliasRootPackage, '1.2.3', '1.2.3');
629
        $packageVersionsRootPackage              = new RootPackage('ocramius/package-versions', '1.2.3', '1.2.3');
630
        $aliasPackageVersionsRootPackage         = new RootAliasPackage($packageVersionsRootPackage, '1.2.3', '1.2.3');
631
        $indirectAliasPackageVersionsRootPackage = new RootAliasPackage(
632
            $aliasPackageVersionsRootPackage,
633
            '1.2.3',
634
            '1.2.3'
635
        );
636
637
        return [
638
            'root package is not ocramius/package-versions' => [
639
                $baseRootPackage,
640
                true,
641
            ],
642
            'alias root package is not ocramius/package-versions' => [
643
                $aliasRootPackage,
644
                true,
645
            ],
646
            'indirect alias root package is not ocramius/package-versions' => [
647
                $indirectAliasRootPackage,
648
                true,
649
            ],
650
            'root package is ocramius/package-versions' => [
651
                $packageVersionsRootPackage,
652
                false,
653
            ],
654
            'alias root package is ocramius/package-versions' => [
655
                $aliasPackageVersionsRootPackage,
656
                false,
657
            ],
658
            'indirect alias root package is ocramius/package-versions' => [
659
                $indirectAliasPackageVersionsRootPackage,
660
                false,
661
            ],
662
        ];
663
    }
664
665
    public function testVersionsAreNotDumpedIfPackageVersionsNotExplicitlyRequired() : void
666
    {
667
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
668
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
669
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
670
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
671
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
672
        $package           = $this->createMock(RootPackageInterface::class);
673
674
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
675
676
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
677
678
        /** @noinspection MkdirRaceConditionInspection */
679
        mkdir($expectedPath, 0777, true);
680
681
        $locker
682
            ->expects(self::any())
683
            ->method('getLockData')
684
            ->willReturn([
685
                'packages' => [
686
                    [
687
                        'name'    => 'foo/bar',
688
                        'version' => '1.2.3',
689
                        'dist'  => ['reference' => 'abc123'], // version defined in the dist, this time
690
                    ],
691
                    [
692
                        'name'    => 'baz/tab',
693
                        'version' => '4.5.6', // source missing
694
                    ],
695
                ],
696
            ]);
697
698
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
699
700
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
701
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
702
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
703
        $this->composer->expects(self::any())->method('getPackage')->willReturn($package);
704
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
705
706
        $package->expects(self::any())->method('getName')->willReturn('root/package');
707
        $package->expects(self::any())->method('getVersion')->willReturn('1.3.5');
708
        $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd');
709
        $package->expects(self::any())->method('getReplaces')->willReturn([]);
710
711
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
712
713
        Installer::dumpVersionsClass(new Event(
714
            'post-install-cmd',
715
            $this->composer,
716
            $this->io
717
        ));
718
719
        self::assertFileDoesNotExist($expectedPath . '/Versions.php');
720
721
        $this->rmDir($vendorDir);
722
    }
723
724
    /**
725
     * @group #41
726
     * @group #46
727
     */
728
    public function testVersionsAreNotDumpedIfPackageIsScheduledForRemoval() : void
729
    {
730
        $config  = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
731
        $locker  = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
732
        $package = $this->createMock(RootPackageInterface::class);
733
        $package->expects(self::any())->method('getReplaces')->willReturn([]);
734
        $vendorDir    = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
735
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
736
737
        $locker
738
            ->expects(self::any())
739
            ->method('getLockData')
740
            ->willReturn([
741
                'packages' => [
742
                    [
743
                        'name'    => 'ocramius/package-versions',
744
                        'version' => '1.0.0',
745
                    ],
746
                ],
747
            ]);
748
749
        $package->expects(self::any())->method('getName')->willReturn('root/package');
750
751
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
752
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
753
        $this->composer->expects(self::any())->method('getPackage')->willReturn($package);
754
755
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
756
757
        Installer::dumpVersionsClass(new Event(
758
            'post-install-cmd',
759
            $this->composer,
760
            $this->io
761
        ));
762
763
        self::assertFileDoesNotExist($expectedPath . '/Versions.php');
764
        self::assertFileDoesNotExist($expectedPath . '/Versions.php');
765
    }
766
767
    public function testGeneratedVersionFileAccessRights() : void
768
    {
769
        if (strpos(PHP_OS, 'WIN') === 0) {
770
            $this->markTestSkipped('Windows is kinda "meh" at file access levels');
771
        }
772
773
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
774
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
775
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
776
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
777
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
778
        $package           = $this->createMock(RootPackageInterface::class);
779
        $package->expects(self::any())->method('getReplaces')->willReturn([]);
780
781
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
782
783
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
784
785
        /** @noinspection MkdirRaceConditionInspection */
786
        mkdir($expectedPath, 0777, true);
787
788
        $locker
789
            ->expects(self::any())
790
            ->method('getLockData')
791
            ->willReturn([
792
                'packages' => [
793
                    [
794
                        'name'    => 'ocramius/package-versions',
795
                        'version' => '1.0.0',
796
                    ],
797
                ],
798
            ]);
799
800
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
801
802
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
803
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
804
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
805
        $this->composer->expects(self::any())->method('getPackage')->willReturn($package);
806
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
807
808
        $package->expects(self::any())->method('getName')->willReturn('root/package');
809
        $package->expects(self::any())->method('getVersion')->willReturn('1.3.5');
810
        $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd');
811
812
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
813
814
        Installer::dumpVersionsClass(new Event(
815
            'post-install-cmd',
816
            $this->composer,
817
            $this->io
818
        ));
819
820
        $filePath = $expectedPath . '/Versions.php';
821
822
        self::assertFileExists($filePath);
823
        self::assertSame('0664', substr(sprintf('%o', fileperms($filePath)), -4));
824
825
        $this->rmDir($vendorDir);
826
    }
827
828
    private function rmDir(string $directory) : void
829
    {
830
        if (! is_dir($directory)) {
831
            unlink($directory);
832
833
            return;
834
        }
835
836
        array_map(
837
            function ($item) use ($directory) : void {
838
                $this->rmDir($directory . '/' . $item);
839
            },
840
            array_filter(
841
                scandir($directory),
0 ignored issues
show
Bug introduced by
It seems like scandir($directory) can also be of type false; however, parameter $input of array_filter() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

841
                /** @scrutinizer ignore-type */ scandir($directory),
Loading history...
842
                static function (string $dirItem) {
843
                    return ! in_array($dirItem, ['.', '..'], true);
844
                }
845
            )
846
        );
847
848
        rmdir($directory);
849
    }
850
851
    /**
852
     * @group composer/composer#5237
853
     */
854
    public function testWillEscapeRegexParsingOfClassDefinitions() : void
855
    {
856
        self::assertSame(
857
            1,
858
            preg_match_all(
859
                '{^((?:final\s+)?(?:\s*))class\s+(\S+)}mi',
860
                file_get_contents((new ReflectionClass(Installer::class))->getFileName())
861
            )
862
        );
863
    }
864
865
    public function testGetVersionsIsNotNormalizedForRootPackage() : void
866
    {
867
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
868
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
869
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
870
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
871
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
872
873
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
874
875
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
876
877
        /** @noinspection MkdirRaceConditionInspection */
878
        mkdir($expectedPath, 0777, true);
879
880
        $locker
881
            ->expects(self::any())
882
            ->method('getLockData')
883
            ->willReturn([
884
                'packages' => [
885
                    [
886
                        'name'    => 'ocramius/package-versions',
887
                        'version' => '1.0.0',
888
                    ],
889
                ],
890
            ]);
891
892
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
893
894
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
895
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
896
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
897
        $this->composer->expects(self::any())->method('getPackage')->willReturn($this->getRootPackageMock());
898
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
899
900
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
901
902
        Installer::dumpVersionsClass(new Event(
903
            'post-install-cmd',
904
            $this->composer,
905
            $this->io
906
        ));
907
908
        $expectedSource = <<<'PHP'
909
<?php
910
911
declare(strict_types=1);
912
913
namespace PackageVersions;
914
915
use OutOfBoundsException;
916
917
/**
918
 * This class is generated by ocramius/package-versions, specifically by
919
 * @see \PackageVersions\Installer
920
 *
921
 * This file is overwritten at every run of `composer install` or `composer update`.
922
 */
923
final class Versions
924
{
925
    public const ROOT_PACKAGE_NAME = 'root/package';
926
    /**
927
     * Array of all available composer packages.
928
     * Dont read this array from your calling code, but use the \PackageVersions\Versions::getVersion() method instead.
929
     *
930
     * @var array<string, string>
931
     * @internal
932
     */
933
    public const VERSIONS          = array (
934
  'ocramius/package-versions' => '1.0.0@',
935
  'some-replaced/package' => '1.3.5@aaabbbcccddd',
936
  'root/package' => '1.3.5@aaabbbcccddd',
937
);
938
939
    private function __construct()
940
    {
941
    }
942
943
    /**
944
     * @throws OutOfBoundsException If a version cannot be located.
945
     *
946
     * @psalm-param key-of<self::VERSIONS> $packageName
947
     * @psalm-pure
948
     */
949
    public static function getVersion(string $packageName) : string
950
    {
951
        if (isset(self::VERSIONS[$packageName])) {
952
            return self::VERSIONS[$packageName];
953
        }
954
955
        throw new OutOfBoundsException(
956
            'Required package "' . $packageName . '" is not installed: check your ./vendor/composer/installed.json and/or ./composer.lock files'
957
        );
958
    }
959
}
960
961
PHP;
962
963
        self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php'));
964
965
        $this->rmDir($vendorDir);
966
    }
967
968
    private function getRootPackageMock() : RootPackageInterface
969
    {
970
        $package = $this->createMock(RootPackageInterface::class);
971
        $package->expects(self::any())->method('getName')->willReturn('root/package');
972
        $package->expects(self::any())->method('getPrettyVersion')->willReturn('1.3.5');
973
        $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd');
974
975
        $link = $this->createMock(Link::class);
976
        $link->expects(self::any())->method('getTarget')->willReturn('some-replaced/package');
977
        $link->expects(self::any())->method('getPrettyConstraint')->willReturn('self.version');
978
979
        $package->expects(self::any())->method('getReplaces')->willReturn([$link]);
980
981
        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...
982
    }
983
}
984