Completed
Pull Request — master (#39)
by Aydin
06:11
created

InstallerTest   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 598
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 9
Bugs 0 Features 0
Metric Value
wmc 13
c 9
b 0
f 0
lcom 1
cbo 8
dl 0
loc 598
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A testGetSubscribedEvents() 0 16 2
A setUp() 0 11 1
B testDumpVersionsClassNoDev() 0 106 1
B testDumpVersionsWithoutPackageSourceDetails() 0 103 1
A testDumpsVersionsClassToSpecificLocation() 0 54 2
B rootPackageProvider() 0 40 1
B testDumpVersionsClass() 0 116 1
A testVersionsAreNotDumpedIfPackageVersionsNotExplicitlyRequired() 0 58 1
A rmDir() 0 22 2
A testWillEscapeRegexParsingOfClassDefinitions() 0 10 1
1
<?php
2
3
namespace PackageVersionsTest;
4
5
use Composer\Composer;
6
use Composer\Config;
7
use Composer\EventDispatcher\EventDispatcher;
8
use Composer\Installer\InstallationManager;
9
use Composer\IO\IOInterface;
10
use Composer\Package\Locker;
11
use Composer\Package\RootAliasPackage;
12
use Composer\Package\RootPackage;
13
use Composer\Package\RootPackageInterface;
14
use Composer\Repository\InstalledRepositoryInterface;
15
use Composer\Repository\RepositoryManager;
16
use Composer\Script\Event;
17
use PackageVersions\Installer;
18
use PHPUnit_Framework_TestCase;
19
20
/**
21
 * @covers \PackageVersions\Installer
22
 */
23
final class InstallerTest extends PHPUnit_Framework_TestCase
24
{
25
    /**
26
     * @var Composer|\PHPUnit_Framework_MockObject_MockObject
27
     */
28
    private $composer;
29
30
    /**
31
     * @var EventDispatcher|\PHPUnit_Framework_MockObject_MockObject
32
     */
33
    private $eventDispatcher;
34
35
    /**
36
     * @var IOInterface|\PHPUnit_Framework_MockObject_MockObject
37
     */
38
    private $io;
39
40
    /**
41
     * @var Installer
42
     */
43
    private $installer;
44
45
    /**
46
     * {@inheritDoc}
47
     *
48
     * @throws \PHPUnit_Framework_Exception
49
     */
50
    protected function setUp()
51
    {
52
        parent::setUp();
53
54
        $this->installer       = new Installer();
55
        $this->io              = $this->createMock(IOInterface::class);
56
        $this->composer        = $this->createMock(Composer::class);
57
        $this->eventDispatcher = $this->getMockBuilder(EventDispatcher::class)->disableOriginalConstructor()->getMock();
58
59
        $this->composer->expects(self::any())->method('getEventDispatcher')->willReturn($this->eventDispatcher);
60
    }
61
62
    public function testGetSubscribedEvents()
63
    {
64
        $events = Installer::getSubscribedEvents();
65
66
        self::assertSame(
67
            [
68
                'post-install-cmd' => 'dumpVersionsClass',
69
                'post-update-cmd'  => 'dumpVersionsClass',
70
            ],
71
            $events
72
        );
73
74
        foreach ($events as $callback) {
75
            self::assertInternalType('callable', [$this->installer, $callback]);
76
        }
77
    }
78
79
    public function testDumpVersionsClass()
80
    {
81
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
82
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
83
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
84
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
85
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
86
        $package           = $this->createMock(RootPackageInterface::class);
87
88
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
89
90
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
91
92
        mkdir($expectedPath, 0777, true);
93
94
        $locker
95
            ->expects(self::any())
96
            ->method('getLockData')
97
            ->willReturn([
98
                'packages' => [
99
                    [
100
                        'name'    => 'ocramius/package-versions',
101
                        'version' => '1.0.0',
102
                    ],
103
                    [
104
                        'name'    => 'foo/bar',
105
                        'version' => '1.2.3',
106
                        'source'  => [
107
                            'reference' => 'abc123',
108
                        ],
109
                    ],
110
                    [
111
                        'name'    => 'baz/tab',
112
                        'version' => '4.5.6',
113
                        'source'  => [
114
                            'reference' => 'def456',
115
                        ],
116
                    ],
117
                ],
118
                'packages-dev' => [
119
                    [
120
                        'name'    => 'tar/taz',
121
                        'version' => '7.8.9',
122
                        'source'  => [
123
                            'reference' => 'ghi789',
124
                        ],
125
                    ]
126
                ],
127
            ]);
128
129
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
130
131
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
132
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
133
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
134
        $this->composer->expects(self::any())->method('getPackage')->willReturn($package);
135
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
136
137
        $package->expects(self::any())->method('getName')->willReturn('root/package');
138
        $package->expects(self::any())->method('getVersion')->willReturn('1.3.5');
139
        $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd');
140
141
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
142
143
        Installer::dumpVersionsClass(new Event(
144
            'post-install-cmd',
145
            $this->composer,
146
            $this->io
147
        ));
148
149
        $expectedSource = <<<'PHP'
150
<?php
151
152
namespace PackageVersions;
153
154
/**
155
 * This class is generated by ocramius/package-versions, specifically by
156
 * @see \PackageVersions\Installer
157
 *
158
 * This file is overwritten at every run of `composer install` or `composer update`.
159
 */
160
final class Versions
161
{
162
    const VERSIONS = array (
163
  'ocramius/package-versions' => '1.0.0@',
164
  'foo/bar' => '1.2.3@abc123',
165
  'baz/tab' => '4.5.6@def456',
166
  'tar/taz' => '7.8.9@ghi789',
167
  'root/package' => '1.3.5@aaabbbcccddd',
168
);
169
170
    private function __construct()
171
    {
172
    }
173
174
    /**
175
     * @throws \OutOfBoundsException if a version cannot be located
176
     */
177
    public static function getVersion(string $packageName) : string
178
    {
179
        if (! isset(self::VERSIONS[$packageName])) {
180
            throw new \OutOfBoundsException(
181
                'Required package "' . $packageName . '" is not installed: cannot detect its version'
182
            );
183
        }
184
185
        return self::VERSIONS[$packageName];
186
    }
187
}
188
189
PHP;
190
191
        self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php'));
192
193
        $this->rmDir($vendorDir);
194
    }
195
196
    public function testDumpVersionsClassNoDev()
197
    {
198
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
199
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
200
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
201
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
202
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
203
        $package           = $this->createMock(RootPackageInterface::class);
204
205
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
206
207
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
208
209
        mkdir($expectedPath, 0777, true);
210
211
        $locker
212
            ->expects(self::any())
213
            ->method('getLockData')
214
            ->willReturn([
215
                'packages' => [
216
                    [
217
                        'name'    => 'ocramius/package-versions',
218
                        'version' => '1.0.0',
219
                    ],
220
                    [
221
                        'name'    => 'foo/bar',
222
                        'version' => '1.2.3',
223
                        'source'  => [
224
                            'reference' => 'abc123',
225
                        ],
226
                    ],
227
                    [
228
                        'name'    => 'baz/tab',
229
                        'version' => '4.5.6',
230
                        'source'  => [
231
                            'reference' => 'def456',
232
                        ],
233
                    ],
234
                ],
235
            ]);
236
237
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
238
239
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
240
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
241
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
242
        $this->composer->expects(self::any())->method('getPackage')->willReturn($package);
243
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
244
245
        $package->expects(self::any())->method('getName')->willReturn('root/package');
246
        $package->expects(self::any())->method('getVersion')->willReturn('1.3.5');
247
        $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd');
248
249
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
250
251
        Installer::dumpVersionsClass(new Event(
252
            'post-install-cmd',
253
            $this->composer,
254
            $this->io
255
        ));
256
257
        $expectedSource = <<<'PHP'
258
<?php
259
260
namespace PackageVersions;
261
262
/**
263
 * This class is generated by ocramius/package-versions, specifically by
264
 * @see \PackageVersions\Installer
265
 *
266
 * This file is overwritten at every run of `composer install` or `composer update`.
267
 */
268
final class Versions
269
{
270
    const VERSIONS = array (
271
  'ocramius/package-versions' => '1.0.0@',
272
  'foo/bar' => '1.2.3@abc123',
273
  'baz/tab' => '4.5.6@def456',
274
  'root/package' => '1.3.5@aaabbbcccddd',
275
);
276
277
    private function __construct()
278
    {
279
    }
280
281
    /**
282
     * @throws \OutOfBoundsException if a version cannot be located
283
     */
284
    public static function getVersion(string $packageName) : string
285
    {
286
        if (! isset(self::VERSIONS[$packageName])) {
287
            throw new \OutOfBoundsException(
288
                'Required package "' . $packageName . '" is not installed: cannot detect its version'
289
            );
290
        }
291
292
        return self::VERSIONS[$packageName];
293
    }
294
}
295
296
PHP;
297
298
        self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php'));
299
300
        $this->rmDir($vendorDir);
301
    }
302
303
    /**
304
     * @group #12
305
     *
306
     * @throws \RuntimeException
307
     */
308
    public function testDumpVersionsWithoutPackageSourceDetails()
309
    {
310
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
311
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
312
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
313
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
314
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
315
        $package           = $this->createMock(RootPackageInterface::class);
316
317
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
318
319
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
320
321
        mkdir($expectedPath, 0777, true);
322
323
        $locker
324
            ->expects(self::any())
325
            ->method('getLockData')
326
            ->willReturn([
327
                'packages' => [
328
                    [
329
                        'name'    => 'ocramius/package-versions',
330
                        'version' => '1.0.0',
331
                    ],
332
                    [
333
                        'name'    => 'foo/bar',
334
                        'version' => '1.2.3',
335
                        'dist'  => [
336
                            'reference' => 'abc123', // version defined in the dist, this time
337
                        ],
338
                    ],
339
                    [
340
                        'name'    => 'baz/tab',
341
                        'version' => '4.5.6', // source missing
342
                    ],
343
                ],
344
            ]);
345
346
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
347
348
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
349
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
350
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
351
        $this->composer->expects(self::any())->method('getPackage')->willReturn($package);
352
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
353
354
        $package->expects(self::any())->method('getName')->willReturn('root/package');
355
        $package->expects(self::any())->method('getVersion')->willReturn('1.3.5');
356
        $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd');
357
358
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
359
360
        Installer::dumpVersionsClass(new Event(
361
            'post-install-cmd',
362
            $this->composer,
363
            $this->io
364
        ));
365
366
        $expectedSource = <<<'PHP'
367
<?php
368
369
namespace PackageVersions;
370
371
/**
372
 * This class is generated by ocramius/package-versions, specifically by
373
 * @see \PackageVersions\Installer
374
 *
375
 * This file is overwritten at every run of `composer install` or `composer update`.
376
 */
377
final class Versions
378
{
379
    const VERSIONS = array (
380
  'ocramius/package-versions' => '1.0.0@',
381
  'foo/bar' => '1.2.3@abc123',
382
  'baz/tab' => '4.5.6@',
383
  'root/package' => '1.3.5@aaabbbcccddd',
384
);
385
386
    private function __construct()
387
    {
388
    }
389
390
    /**
391
     * @throws \OutOfBoundsException if a version cannot be located
392
     */
393
    public static function getVersion(string $packageName) : string
394
    {
395
        if (! isset(self::VERSIONS[$packageName])) {
396
            throw new \OutOfBoundsException(
397
                'Required package "' . $packageName . '" is not installed: cannot detect its version'
398
            );
399
        }
400
401
        return self::VERSIONS[$packageName];
402
    }
403
}
404
405
PHP;
406
407
        self::assertSame($expectedSource, file_get_contents($expectedPath . '/Versions.php'));
408
409
        $this->rmDir($vendorDir);
410
    }
411
412
    /**
413
     * @dataProvider rootPackageProvider
414
     *
415
     * @param RootPackageInterface $rootPackage
416
     * @param bool                 $inVendor
417
     *
418
     * @throws \RuntimeException
419
     */
420
    public function testDumpsVersionsClassToSpecificLocation(RootPackageInterface $rootPackage, bool $inVendor)
421
    {
422
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
423
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
424
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
425
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
426
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
427
428
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true) . '/vendor';
429
430
        mkdir($vendorDir, 0777, true);
431
432
        $expectedPath = $inVendor
433
            ? $vendorDir . '/ocramius/package-versions/src/PackageVersions'
434
            : realpath($vendorDir . '/..') . '/src/PackageVersions';
435
436
        mkdir($expectedPath, 0777, true);
437
438
        $locker
439
            ->expects(self::any())
440
            ->method('getLockData')
441
            ->willReturn([
442
                'packages' => [
443
                    [
444
                        'name'    => 'ocramius/package-versions',
445
                        'version' => '1.0.0',
446
                    ]
447
                ],
448
                'packages-dev' => [],
449
            ]);
450
451
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
452
453
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
454
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
455
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
456
        $this->composer->expects(self::any())->method('getPackage')->willReturn($rootPackage);
457
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
458
459
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
460
461
        Installer::dumpVersionsClass(new Event(
462
            'post-install-cmd',
463
            $this->composer,
464
            $this->io
465
        ));
466
467
        self::assertStringMatchesFormat(
468
            '%Aclass Versions%A1.2.3@%A',
469
            file_get_contents($expectedPath . '/Versions.php')
470
        );
471
472
        $this->rmDir($vendorDir);
473
    }
474
475
    /**
476
     * @return bool[][]|RootPackageInterface[][] the root package and whether the versions class is to be generated in
477
     *                                           the vendor dir or not
478
     */
479
    public function rootPackageProvider() : array
480
    {
481
        $baseRootPackage                         = new RootPackage('root/package', '1.2.3', '1.2.3');
482
        $aliasRootPackage                        = new RootAliasPackage($baseRootPackage, '1.2.3', '1.2.3');
483
        $indirectAliasRootPackage                = new RootAliasPackage($aliasRootPackage, '1.2.3', '1.2.3');
484
        $packageVersionsRootPackage              = new RootPackage('ocramius/package-versions', '1.2.3', '1.2.3');
485
        $aliasPackageVersionsRootPackage         = new RootAliasPackage($packageVersionsRootPackage, '1.2.3', '1.2.3');
486
        $indirectAliasPackageVersionsRootPackage = new RootAliasPackage(
487
            $aliasPackageVersionsRootPackage,
488
            '1.2.3',
489
            '1.2.3'
490
        );
491
492
        return [
493
            'root package is not ocramius/package-versions' => [
494
                $baseRootPackage,
495
                true
496
            ],
497
            'alias root package is not ocramius/package-versions' => [
498
                $aliasRootPackage,
499
                true
500
            ],
501
            'indirect alias root package is not ocramius/package-versions' => [
502
                $indirectAliasRootPackage,
503
                true
504
            ],
505
            'root package is ocramius/package-versions' => [
506
                $packageVersionsRootPackage,
507
                false
508
            ],
509
            'alias root package is ocramius/package-versions' => [
510
                $aliasPackageVersionsRootPackage,
511
                false
512
            ],
513
            'indirect alias root package is ocramius/package-versions' => [
514
                $indirectAliasPackageVersionsRootPackage,
515
                false
516
            ],
517
        ];
518
    }
519
520
    public function testVersionsAreNotDumpedIfPackageVersionsNotExplicitlyRequired()
521
    {
522
        $config            = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
523
        $locker            = $this->getMockBuilder(Locker::class)->disableOriginalConstructor()->getMock();
524
        $repositoryManager = $this->getMockBuilder(RepositoryManager::class)->disableOriginalConstructor()->getMock();
525
        $installManager    = $this->getMockBuilder(InstallationManager::class)->disableOriginalConstructor()->getMock();
526
        $repository        = $this->createMock(InstalledRepositoryInterface::class);
527
        $package           = $this->createMock(RootPackageInterface::class);
528
529
        $vendorDir = sys_get_temp_dir() . '/' . uniqid('InstallerTest', true);
530
531
        $expectedPath = $vendorDir . '/ocramius/package-versions/src/PackageVersions';
532
533
        mkdir($expectedPath, 0777, true);
534
535
        $locker
536
            ->expects(self::any())
537
            ->method('getLockData')
538
            ->willReturn([
539
                 'packages' => [
540
                     [
541
                         'name'    => 'foo/bar',
542
                         'version' => '1.2.3',
543
                         'dist'  => [
544
                             'reference' => 'abc123', // version defined in the dist, this time
545
                         ],
546
                     ],
547
                     [
548
                         'name'    => 'baz/tab',
549
                         'version' => '4.5.6', // source missing
550
                     ],
551
                 ],
552
             ]);
553
554
        $repositoryManager->expects(self::any())->method('getLocalRepository')->willReturn($repository);
555
556
        $this->composer->expects(self::any())->method('getConfig')->willReturn($config);
557
        $this->composer->expects(self::any())->method('getLocker')->willReturn($locker);
558
        $this->composer->expects(self::any())->method('getRepositoryManager')->willReturn($repositoryManager);
559
        $this->composer->expects(self::any())->method('getPackage')->willReturn($package);
560
        $this->composer->expects(self::any())->method('getInstallationManager')->willReturn($installManager);
561
562
        $package->expects(self::any())->method('getName')->willReturn('root/package');
563
        $package->expects(self::any())->method('getVersion')->willReturn('1.3.5');
564
        $package->expects(self::any())->method('getSourceReference')->willReturn('aaabbbcccddd');
565
566
        $config->expects(self::any())->method('get')->with('vendor-dir')->willReturn($vendorDir);
567
568
        Installer::dumpVersionsClass(new Event(
569
            'post-install-cmd',
570
            $this->composer,
571
            $this->io
572
        ));
573
574
        self::assertFileNotExists($expectedPath . '/Versions.php');
575
576
        $this->rmDir($vendorDir);
577
    }
578
579
    /**
580
     * @param string $directory
581
     *
582
     * @return void
583
     */
584
    private function rmDir(string $directory)
585
    {
586
        if (! is_dir($directory)) {
587
            unlink($directory);
588
589
            return;
590
        }
591
592
        array_map(
593
            function ($item) use ($directory) {
594
                $this->rmDir($directory . '/' . $item);
595
            },
596
            array_filter(
597
                scandir($directory),
598
                function (string $dirItem) {
599
                    return ! in_array($dirItem, ['.', '..'], true);
600
                }
601
            )
602
        );
603
        
604
        rmdir($directory);
605
    }
606
607
    /**
608
     * @group composer/composer#5237
609
     */
610
    public function testWillEscapeRegexParsingOfClassDefinitions()
611
    {
612
        self::assertSame(
613
            1,
614
            preg_match_all(
615
                '{^((?:final\s+)?(?:\s*))class\s+(\S+)}mi',
616
                file_get_contents((new \ReflectionClass(Installer::class))->getFileName())
617
            )
618
        );
619
    }
620
}
621