2012-04-27 09:42:58 +00:00
|
|
|
<?php
|
2013-05-27 08:41:50 +00:00
|
|
|
|
2012-04-27 09:42:58 +00:00
|
|
|
/*
|
|
|
|
* This file is part of Composer.
|
|
|
|
*
|
|
|
|
* (c) Nils Adermann <naderman@naderman.de>
|
|
|
|
* Jordi Boggiano <j.boggiano@seld.be>
|
|
|
|
*
|
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Composer\Test;
|
|
|
|
|
2020-03-27 20:41:49 +00:00
|
|
|
use Composer\DependencyResolver\Request;
|
2021-11-11 11:24:12 +00:00
|
|
|
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
|
2012-04-27 09:42:58 +00:00
|
|
|
use Composer\Installer;
|
2021-12-07 10:03:51 +00:00
|
|
|
use Composer\Pcre\Preg;
|
2020-06-19 15:56:13 +00:00
|
|
|
use Symfony\Component\Console\Application;
|
|
|
|
use Symfony\Component\Console\Command\Command;
|
|
|
|
use Symfony\Component\Console\Input\InputArgument;
|
|
|
|
use Symfony\Component\Console\Input\InputOption;
|
2018-09-12 11:27:10 +00:00
|
|
|
use Composer\IO\BufferIO;
|
2021-11-02 13:32:09 +00:00
|
|
|
use Composer\Config;
|
2012-05-13 19:40:42 +00:00
|
|
|
use Composer\Json\JsonFile;
|
2018-09-13 13:23:05 +00:00
|
|
|
use Composer\Package\Dumper\ArrayDumper;
|
2016-04-01 11:07:42 +00:00
|
|
|
use Composer\Util\Filesystem;
|
2012-04-27 09:42:58 +00:00
|
|
|
use Composer\Repository\ArrayRepository;
|
|
|
|
use Composer\Repository\RepositoryManager;
|
2021-11-02 13:32:09 +00:00
|
|
|
use Composer\Repository\RepositoryInterface;
|
2013-04-28 20:32:46 +00:00
|
|
|
use Composer\Repository\InstalledArrayRepository;
|
2012-08-23 13:52:40 +00:00
|
|
|
use Composer\Package\RootPackageInterface;
|
2021-10-17 12:43:34 +00:00
|
|
|
use Composer\Package\BasePackage;
|
2021-11-02 13:32:09 +00:00
|
|
|
use Composer\Package\PackageInterface;
|
2012-04-27 09:42:58 +00:00
|
|
|
use Composer\Package\Link;
|
2012-05-13 19:40:42 +00:00
|
|
|
use Composer\Package\Locker;
|
|
|
|
use Composer\Test\Mock\FactoryMock;
|
|
|
|
use Composer\Test\Mock\InstalledFilesystemRepositoryMock;
|
2012-04-27 09:42:58 +00:00
|
|
|
use Composer\Test\Mock\InstallationManagerMock;
|
2020-10-01 14:42:02 +00:00
|
|
|
use Composer\Util\Platform;
|
2012-05-27 22:11:18 +00:00
|
|
|
use Symfony\Component\Console\Input\StringInput;
|
|
|
|
use Symfony\Component\Console\Output\StreamOutput;
|
2016-01-28 13:41:19 +00:00
|
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
|
|
use Symfony\Component\Console\Formatter\OutputFormatter;
|
2012-04-27 09:42:58 +00:00
|
|
|
|
|
|
|
class InstallerTest extends TestCase
|
|
|
|
{
|
2021-11-02 13:32:09 +00:00
|
|
|
/** @var string */
|
2013-03-03 16:18:50 +00:00
|
|
|
protected $prevCwd;
|
2021-11-02 13:32:09 +00:00
|
|
|
/** @var ?string */
|
2016-04-01 11:07:42 +00:00
|
|
|
protected $tempComposerHome;
|
2013-03-03 16:18:50 +00:00
|
|
|
|
2021-12-08 16:03:05 +00:00
|
|
|
public function setUp(): void
|
2013-03-03 16:18:50 +00:00
|
|
|
{
|
|
|
|
$this->prevCwd = getcwd();
|
|
|
|
chdir(__DIR__);
|
|
|
|
}
|
|
|
|
|
2021-12-08 16:03:05 +00:00
|
|
|
public function tearDown(): void
|
2013-03-03 16:18:50 +00:00
|
|
|
{
|
2020-10-01 14:42:02 +00:00
|
|
|
Platform::clearEnv('COMPOSER_POOL_OPTIMIZER');
|
|
|
|
|
2013-03-03 16:18:50 +00:00
|
|
|
chdir($this->prevCwd);
|
2021-08-05 04:18:14 +00:00
|
|
|
if (isset($this->tempComposerHome) && is_dir($this->tempComposerHome)) {
|
2016-04-01 11:07:42 +00:00
|
|
|
$fs = new Filesystem;
|
|
|
|
$fs->removeDirectory($this->tempComposerHome);
|
|
|
|
}
|
2013-03-03 16:18:50 +00:00
|
|
|
}
|
|
|
|
|
2012-04-27 09:42:58 +00:00
|
|
|
/**
|
|
|
|
* @dataProvider provideInstaller
|
2021-10-17 12:43:34 +00:00
|
|
|
* @param RootPackageInterface&BasePackage $rootPackage
|
2021-11-02 13:32:09 +00:00
|
|
|
* @param RepositoryInterface[] $repositories
|
2021-11-14 19:42:24 +00:00
|
|
|
* @param mixed[] $options
|
2012-04-27 09:42:58 +00:00
|
|
|
*/
|
2012-08-23 13:52:40 +00:00
|
|
|
public function testInstaller(RootPackageInterface $rootPackage, $repositories, array $options)
|
2012-04-27 09:42:58 +00:00
|
|
|
{
|
2018-09-12 11:27:10 +00:00
|
|
|
$io = new BufferIO('', OutputInterface::VERBOSITY_NORMAL, new OutputFormatter(false));
|
2012-04-27 09:42:58 +00:00
|
|
|
|
2018-04-12 08:24:56 +00:00
|
|
|
$downloadManager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
|
|
|
|
->setConstructorArgs(array($io))
|
|
|
|
->getMock();
|
|
|
|
$config = $this->getMockBuilder('Composer\Config')->getMock();
|
2021-08-18 21:35:27 +00:00
|
|
|
$config->expects($this->any())
|
|
|
|
->method('get')
|
|
|
|
->will($this->returnCallback(function ($key) {
|
|
|
|
switch ($key) {
|
|
|
|
case 'vendor-dir':
|
|
|
|
return 'foo';
|
2021-10-27 14:18:24 +00:00
|
|
|
case 'lock':
|
|
|
|
case 'notify-on-install':
|
2021-08-18 21:35:27 +00:00
|
|
|
return true;
|
2021-10-27 14:18:24 +00:00
|
|
|
case 'platform':
|
2021-08-18 21:35:27 +00:00
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new \UnexpectedValueException('Unknown key '.$key);
|
|
|
|
}));
|
2012-04-27 09:42:58 +00:00
|
|
|
|
2018-10-31 11:44:54 +00:00
|
|
|
$eventDispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock();
|
|
|
|
$httpDownloader = $this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock();
|
2018-11-12 14:34:54 +00:00
|
|
|
$repositoryManager = new RepositoryManager($io, $config, $httpDownloader, $eventDispatcher);
|
2013-04-28 20:32:46 +00:00
|
|
|
$repositoryManager->setLocalRepository(new InstalledArrayRepository());
|
2012-04-29 15:29:06 +00:00
|
|
|
|
|
|
|
if (!is_array($repositories)) {
|
|
|
|
$repositories = array($repositories);
|
|
|
|
}
|
|
|
|
foreach ($repositories as $repository) {
|
|
|
|
$repositoryManager->addRepository($repository);
|
|
|
|
}
|
2012-04-27 09:42:58 +00:00
|
|
|
$installationManager = new InstallationManagerMock();
|
2013-01-06 19:34:52 +00:00
|
|
|
|
2018-09-13 13:23:05 +00:00
|
|
|
// emulate a writable lock file
|
2021-06-03 09:29:00 +00:00
|
|
|
/** @var ?string $lockData */
|
2018-09-13 13:23:05 +00:00
|
|
|
$lockData = null;
|
|
|
|
$lockJsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock();
|
|
|
|
$lockJsonMock->expects($this->any())
|
|
|
|
->method('read')
|
2020-11-22 13:48:56 +00:00
|
|
|
->will($this->returnCallback(function () use (&$lockData) {
|
2018-09-13 13:23:05 +00:00
|
|
|
return json_decode($lockData, true);
|
|
|
|
}));
|
|
|
|
$lockJsonMock->expects($this->any())
|
|
|
|
->method('exists')
|
|
|
|
->will($this->returnCallback(function () use (&$lockData) {
|
|
|
|
return $lockData !== null;
|
|
|
|
}));
|
|
|
|
$lockJsonMock->expects($this->any())
|
|
|
|
->method('write')
|
|
|
|
->will($this->returnCallback(function ($value, $options = 0) use (&$lockData) {
|
2019-11-07 21:09:24 +00:00
|
|
|
$lockData = json_encode($value, JsonFile::JSON_PRETTY_PRINT);
|
2018-09-13 13:23:05 +00:00
|
|
|
}));
|
|
|
|
|
|
|
|
$tempLockData = null;
|
2019-09-07 04:03:39 +00:00
|
|
|
$locker = new Locker($io, $lockJsonMock, $installationManager, '{}');
|
2018-09-13 13:23:05 +00:00
|
|
|
|
2013-01-06 19:34:52 +00:00
|
|
|
$autoloadGenerator = $this->getMockBuilder('Composer\Autoload\AutoloadGenerator')->disableOriginalConstructor()->getMock();
|
2012-04-27 09:42:58 +00:00
|
|
|
|
2012-06-24 19:58:51 +00:00
|
|
|
$installer = new Installer($io, $config, clone $rootPackage, $downloadManager, $repositoryManager, $locker, $installationManager, $eventDispatcher, $autoloadGenerator);
|
2012-04-27 09:42:58 +00:00
|
|
|
$result = $installer->run();
|
2018-09-12 11:27:10 +00:00
|
|
|
|
|
|
|
$output = str_replace("\r", '', $io->getOutput());
|
|
|
|
$this->assertEquals(0, $result, $output);
|
2012-04-27 09:42:58 +00:00
|
|
|
|
2017-03-08 14:07:29 +00:00
|
|
|
$expectedInstalled = isset($options['install']) ? $options['install'] : array();
|
|
|
|
$expectedUpdated = isset($options['update']) ? $options['update'] : array();
|
2012-04-29 15:29:06 +00:00
|
|
|
$expectedUninstalled = isset($options['uninstall']) ? $options['uninstall'] : array();
|
|
|
|
|
2012-04-27 09:42:58 +00:00
|
|
|
$installed = $installationManager->getInstalledPackages();
|
2018-09-13 13:23:05 +00:00
|
|
|
$this->assertEquals($this->makePackagesComparable($expectedInstalled), $this->makePackagesComparable($installed));
|
2012-04-27 09:42:58 +00:00
|
|
|
|
|
|
|
$updated = $installationManager->getUpdatedPackages();
|
2012-04-29 15:29:06 +00:00
|
|
|
$this->assertSame($expectedUpdated, $updated);
|
2012-04-27 09:42:58 +00:00
|
|
|
|
|
|
|
$uninstalled = $installationManager->getUninstalledPackages();
|
2012-04-29 15:29:06 +00:00
|
|
|
$this->assertSame($expectedUninstalled, $uninstalled);
|
2012-04-27 09:42:58 +00:00
|
|
|
}
|
|
|
|
|
2021-11-02 13:32:09 +00:00
|
|
|
/**
|
|
|
|
* @param PackageInterface[] $packages
|
|
|
|
* @return mixed[]
|
|
|
|
*/
|
2018-09-13 13:23:05 +00:00
|
|
|
protected function makePackagesComparable($packages)
|
|
|
|
{
|
|
|
|
$dumper = new ArrayDumper();
|
|
|
|
|
2019-11-07 21:01:37 +00:00
|
|
|
$comparable = array();
|
2018-09-13 13:23:05 +00:00
|
|
|
foreach ($packages as $package) {
|
|
|
|
$comparable[] = $dumper->dump($package);
|
|
|
|
}
|
2020-11-22 13:48:56 +00:00
|
|
|
|
2018-09-13 13:23:05 +00:00
|
|
|
return $comparable;
|
|
|
|
}
|
|
|
|
|
2012-04-27 09:42:58 +00:00
|
|
|
public function provideInstaller()
|
|
|
|
{
|
|
|
|
$cases = array();
|
|
|
|
|
|
|
|
// when A requires B and B requires A, and A is a non-published root package
|
|
|
|
// the install of B should succeed
|
|
|
|
|
2012-08-23 13:52:40 +00:00
|
|
|
$a = $this->getPackage('A', '1.0.0', 'Composer\Package\RootPackage');
|
2012-04-27 09:42:58 +00:00
|
|
|
$a->setRequires(array(
|
2020-09-15 08:44:28 +00:00
|
|
|
'b' => new Link('A', 'B', $v = $this->getVersionConstraint('=', '1.0.0'), Link::TYPE_REQUIRE, $v->getPrettyString()),
|
2012-04-27 09:42:58 +00:00
|
|
|
));
|
|
|
|
$b = $this->getPackage('B', '1.0.0');
|
|
|
|
$b->setRequires(array(
|
2020-09-15 08:44:28 +00:00
|
|
|
'a' => new Link('B', 'A', $v = $this->getVersionConstraint('=', '1.0.0'), Link::TYPE_REQUIRE, $v->getPrettyString()),
|
2012-04-27 09:42:58 +00:00
|
|
|
));
|
|
|
|
|
|
|
|
$cases[] = array(
|
|
|
|
$a,
|
|
|
|
new ArrayRepository(array($b)),
|
2012-04-29 15:29:06 +00:00
|
|
|
array(
|
2015-09-28 09:51:14 +00:00
|
|
|
'install' => array($b),
|
2012-04-29 15:29:06 +00:00
|
|
|
),
|
2012-04-27 09:42:58 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// #480: when A requires B and B requires A, and A is a published root package
|
|
|
|
// only B should be installed, as A is the root
|
|
|
|
|
2012-08-23 13:52:40 +00:00
|
|
|
$a = $this->getPackage('A', '1.0.0', 'Composer\Package\RootPackage');
|
2012-04-27 09:42:58 +00:00
|
|
|
$a->setRequires(array(
|
2020-09-15 08:44:28 +00:00
|
|
|
'b' => new Link('A', 'B', $v = $this->getVersionConstraint('=', '1.0.0'), Link::TYPE_REQUIRE, $v->getPrettyString()),
|
2012-04-27 09:42:58 +00:00
|
|
|
));
|
|
|
|
$b = $this->getPackage('B', '1.0.0');
|
|
|
|
$b->setRequires(array(
|
2020-09-15 08:44:28 +00:00
|
|
|
'a' => new Link('B', 'A', $v = $this->getVersionConstraint('=', '1.0.0'), Link::TYPE_REQUIRE, $v->getPrettyString()),
|
2012-04-27 09:42:58 +00:00
|
|
|
));
|
|
|
|
|
|
|
|
$cases[] = array(
|
|
|
|
$a,
|
|
|
|
new ArrayRepository(array($a, $b)),
|
2012-04-29 15:29:06 +00:00
|
|
|
array(
|
2015-09-28 09:51:14 +00:00
|
|
|
'install' => array($b),
|
2012-04-29 15:29:06 +00:00
|
|
|
),
|
2012-04-27 09:42:58 +00:00
|
|
|
);
|
|
|
|
|
2018-09-13 13:23:05 +00:00
|
|
|
// TODO why are there not more cases with uninstall/update?
|
2012-04-27 09:42:58 +00:00
|
|
|
return $cases;
|
|
|
|
}
|
2012-05-13 19:40:42 +00:00
|
|
|
|
2020-10-22 08:14:17 +00:00
|
|
|
/**
|
|
|
|
* @group slow
|
2021-11-02 13:32:09 +00:00
|
|
|
* @dataProvider provideSlowIntegrationTests
|
|
|
|
* @param string $file
|
|
|
|
* @param string $message
|
|
|
|
* @param ?string $condition
|
|
|
|
* @param Config $composerConfig
|
|
|
|
* @param ?mixed[] $lock
|
|
|
|
* @param ?mixed[] $installed
|
|
|
|
* @param string $run
|
|
|
|
* @param mixed[]|false $expectLock
|
|
|
|
* @param ?mixed[] $expectInstalled
|
|
|
|
* @param ?string $expectOutput
|
2020-10-01 14:42:02 +00:00
|
|
|
* @param ?string $expectOutputOptimized
|
2021-11-02 13:32:09 +00:00
|
|
|
* @param string $expect
|
|
|
|
* @param int|string $expectResult
|
2020-10-22 08:14:17 +00:00
|
|
|
*/
|
2020-10-01 14:42:02 +00:00
|
|
|
public function testSlowIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectInstalled, $expectOutput, $expectOutputOptimized, $expect, $expectResult)
|
2020-10-22 08:14:17 +00:00
|
|
|
{
|
2020-10-01 14:42:02 +00:00
|
|
|
Platform::putEnv('COMPOSER_POOL_OPTIMIZER', '0');
|
|
|
|
|
|
|
|
$this->doTestIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectInstalled, $expectOutput, $expect, $expectResult);
|
2020-10-22 08:14:17 +00:00
|
|
|
}
|
|
|
|
|
2012-05-13 19:40:42 +00:00
|
|
|
/**
|
2021-11-02 13:32:09 +00:00
|
|
|
* @dataProvider provideIntegrationTests
|
2020-10-01 14:42:02 +00:00
|
|
|
* @param string $file
|
|
|
|
* @param string $message
|
|
|
|
* @param ?string $condition
|
|
|
|
* @param Config $composerConfig
|
|
|
|
* @param ?mixed[] $lock
|
|
|
|
* @param ?mixed[] $installed
|
|
|
|
* @param string $run
|
|
|
|
* @param mixed[]|false $expectLock
|
|
|
|
* @param ?mixed[] $expectInstalled
|
|
|
|
* @param ?string $expectOutput
|
|
|
|
* @param ?string $expectOutputOptimized
|
|
|
|
* @param string $expect
|
|
|
|
* @param int|string $expectResult
|
|
|
|
*/
|
|
|
|
public function testIntegrationWithPoolOptimizer($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectInstalled, $expectOutput, $expectOutputOptimized, $expect, $expectResult)
|
|
|
|
{
|
|
|
|
Platform::putEnv('COMPOSER_POOL_OPTIMIZER', '1');
|
|
|
|
|
|
|
|
$this->doTestIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectInstalled, $expectOutputOptimized ?: $expectOutput, $expect, $expectResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dataProvider provideIntegrationTests
|
|
|
|
* @param string $file
|
|
|
|
* @param string $message
|
|
|
|
* @param ?string $condition
|
|
|
|
* @param Config $composerConfig
|
|
|
|
* @param ?mixed[] $lock
|
|
|
|
* @param ?mixed[] $installed
|
|
|
|
* @param string $run
|
|
|
|
* @param mixed[]|false $expectLock
|
|
|
|
* @param ?mixed[] $expectInstalled
|
|
|
|
* @param ?string $expectOutput
|
|
|
|
* @param ?string $expectOutputOptimized
|
|
|
|
* @param string $expect
|
|
|
|
* @param int|string $expectResult
|
|
|
|
*/
|
|
|
|
public function testIntegrationWithRawPool($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectInstalled, $expectOutput, $expectOutputOptimized, $expect, $expectResult)
|
|
|
|
{
|
|
|
|
Platform::putEnv('COMPOSER_POOL_OPTIMIZER', '0');
|
|
|
|
|
|
|
|
$this->doTestIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectInstalled, $expectOutput, $expect, $expectResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-11-02 13:32:09 +00:00
|
|
|
* @param string $file
|
|
|
|
* @param string $message
|
|
|
|
* @param ?string $condition
|
|
|
|
* @param Config $composerConfig
|
|
|
|
* @param ?mixed[] $lock
|
|
|
|
* @param ?mixed[] $installed
|
|
|
|
* @param string $run
|
|
|
|
* @param mixed[]|false $expectLock
|
|
|
|
* @param ?mixed[] $expectInstalled
|
|
|
|
* @param ?string $expectOutput
|
|
|
|
* @param string $expect
|
|
|
|
* @param int|string $expectResult
|
2020-10-01 14:42:02 +00:00
|
|
|
* @return void
|
2012-05-13 19:40:42 +00:00
|
|
|
*/
|
2020-10-01 14:42:02 +00:00
|
|
|
private function doTestIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectInstalled, $expectOutput, $expect, $expectResult)
|
2012-05-13 19:40:42 +00:00
|
|
|
{
|
|
|
|
if ($condition) {
|
|
|
|
eval('$res = '.$condition.';');
|
|
|
|
if (!$res) {
|
|
|
|
$this->markTestSkipped($condition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-28 13:41:19 +00:00
|
|
|
$io = new BufferIO('', OutputInterface::VERBOSITY_NORMAL, new OutputFormatter(false));
|
2012-05-13 19:40:42 +00:00
|
|
|
|
2016-01-27 09:04:45 +00:00
|
|
|
// Prepare for exceptions
|
2016-01-27 12:49:52 +00:00
|
|
|
if (!is_int($expectResult)) {
|
2016-01-27 12:19:08 +00:00
|
|
|
$normalizedOutput = rtrim(str_replace("\n", PHP_EOL, $expect));
|
2016-01-27 12:46:14 +00:00
|
|
|
$this->setExpectedException($expectResult, $normalizedOutput);
|
2016-01-27 12:19:08 +00:00
|
|
|
}
|
2012-05-13 19:40:42 +00:00
|
|
|
|
2016-01-27 12:19:08 +00:00
|
|
|
// Create Composer mock object according to configuration
|
2012-05-28 16:57:59 +00:00
|
|
|
$composer = FactoryMock::create($io, $composerConfig);
|
2016-04-01 11:07:42 +00:00
|
|
|
$this->tempComposerHome = $composer->getConfig()->get('home');
|
2012-05-13 19:40:42 +00:00
|
|
|
|
|
|
|
$jsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock();
|
|
|
|
$jsonMock->expects($this->any())
|
|
|
|
->method('read')
|
|
|
|
->will($this->returnValue($installed));
|
|
|
|
$jsonMock->expects($this->any())
|
|
|
|
->method('exists')
|
|
|
|
->will($this->returnValue(true));
|
|
|
|
|
|
|
|
$repositoryManager = $composer->getRepositoryManager();
|
|
|
|
$repositoryManager->setLocalRepository(new InstalledFilesystemRepositoryMock($jsonMock));
|
|
|
|
|
2018-09-13 13:23:05 +00:00
|
|
|
// emulate a writable lock file
|
2020-11-22 13:48:56 +00:00
|
|
|
$lockData = $lock ? json_encode($lock, JsonFile::JSON_PRETTY_PRINT) : null;
|
2012-05-13 19:40:42 +00:00
|
|
|
$lockJsonMock = $this->getMockBuilder('Composer\Json\JsonFile')->disableOriginalConstructor()->getMock();
|
|
|
|
$lockJsonMock->expects($this->any())
|
|
|
|
->method('read')
|
2020-11-22 13:48:56 +00:00
|
|
|
->will($this->returnCallback(function () use (&$lockData) {
|
2018-09-13 13:23:05 +00:00
|
|
|
return json_decode($lockData, true);
|
|
|
|
}));
|
2012-05-28 16:57:59 +00:00
|
|
|
$lockJsonMock->expects($this->any())
|
|
|
|
->method('exists')
|
2018-09-13 13:23:05 +00:00
|
|
|
->will($this->returnCallback(function () use (&$lockData) {
|
|
|
|
return $lockData !== null;
|
|
|
|
}));
|
|
|
|
$lockJsonMock->expects($this->any())
|
|
|
|
->method('write')
|
|
|
|
->will($this->returnCallback(function ($value, $options = 0) use (&$lockData) {
|
2019-11-07 21:09:24 +00:00
|
|
|
$lockData = json_encode($value, JsonFile::JSON_PRETTY_PRINT);
|
2018-09-13 13:23:05 +00:00
|
|
|
}));
|
2012-05-13 19:40:42 +00:00
|
|
|
|
2012-07-01 16:27:16 +00:00
|
|
|
if ($expectLock) {
|
|
|
|
$actualLock = array();
|
|
|
|
$lockJsonMock->expects($this->atLeastOnce())
|
|
|
|
->method('write')
|
|
|
|
->will($this->returnCallback(function ($hash, $options) use (&$actualLock) {
|
|
|
|
// need to do assertion outside of mock for nice phpunit output
|
|
|
|
// so store value temporarily in reference for later assetion
|
|
|
|
$actualLock = $hash;
|
|
|
|
}));
|
2019-10-20 08:51:59 +00:00
|
|
|
} elseif ($expectLock === false) {
|
|
|
|
$lockJsonMock->expects($this->never())
|
|
|
|
->method('write');
|
2012-07-01 16:27:16 +00:00
|
|
|
}
|
|
|
|
|
2015-08-21 04:54:28 +00:00
|
|
|
$contents = json_encode($composerConfig);
|
2017-12-27 12:20:12 +00:00
|
|
|
$locker = new Locker($io, $lockJsonMock, $composer->getInstallationManager(), $contents);
|
2012-05-13 19:40:42 +00:00
|
|
|
$composer->setLocker($locker);
|
|
|
|
|
2013-08-14 15:42:11 +00:00
|
|
|
$eventDispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock();
|
2018-04-12 08:24:56 +00:00
|
|
|
$autoloadGenerator = $this->getMockBuilder('Composer\Autoload\AutoloadGenerator')
|
|
|
|
->setConstructorArgs(array($eventDispatcher))
|
|
|
|
->getMock();
|
2013-02-24 17:21:16 +00:00
|
|
|
$composer->setAutoloadGenerator($autoloadGenerator);
|
|
|
|
$composer->setEventDispatcher($eventDispatcher);
|
2012-05-13 19:40:42 +00:00
|
|
|
|
2015-02-06 12:52:44 +00:00
|
|
|
$installer = Installer::create($io, $composer);
|
2012-05-13 19:40:42 +00:00
|
|
|
|
2012-05-27 22:11:18 +00:00
|
|
|
$application = new Application;
|
2020-06-19 15:56:13 +00:00
|
|
|
$install = new Command('install');
|
|
|
|
$install->addOption('ignore-platform-reqs', null, InputOption::VALUE_NONE);
|
|
|
|
$install->addOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY);
|
|
|
|
$install->addOption('no-dev', null, InputOption::VALUE_NONE);
|
|
|
|
$install->addOption('dry-run', null, InputOption::VALUE_NONE);
|
|
|
|
$install->setCode(function ($input, $output) use ($installer) {
|
2020-06-02 09:17:49 +00:00
|
|
|
$ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false);
|
2020-06-01 13:43:24 +00:00
|
|
|
|
2013-03-21 11:08:58 +00:00
|
|
|
$installer
|
2014-04-07 09:10:57 +00:00
|
|
|
->setDevMode(!$input->getOption('no-dev'))
|
2014-10-02 05:01:15 +00:00
|
|
|
->setDryRun($input->getOption('dry-run'))
|
2021-11-11 11:24:12 +00:00
|
|
|
->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs));
|
2012-05-13 19:40:42 +00:00
|
|
|
|
2013-11-22 15:17:02 +00:00
|
|
|
return $installer->run();
|
2012-05-27 22:11:18 +00:00
|
|
|
});
|
2020-06-19 15:56:13 +00:00
|
|
|
$application->add($install);
|
|
|
|
|
|
|
|
$update = new Command('update');
|
|
|
|
$update->addOption('ignore-platform-reqs', null, InputOption::VALUE_NONE);
|
|
|
|
$update->addOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY);
|
|
|
|
$update->addOption('no-dev', null, InputOption::VALUE_NONE);
|
|
|
|
$update->addOption('no-install', null, InputOption::VALUE_NONE);
|
|
|
|
$update->addOption('dry-run', null, InputOption::VALUE_NONE);
|
|
|
|
$update->addOption('lock', null, InputOption::VALUE_NONE);
|
|
|
|
$update->addOption('with-all-dependencies', null, InputOption::VALUE_NONE);
|
|
|
|
$update->addOption('with-dependencies', null, InputOption::VALUE_NONE);
|
|
|
|
$update->addOption('prefer-stable', null, InputOption::VALUE_NONE);
|
|
|
|
$update->addOption('prefer-lowest', null, InputOption::VALUE_NONE);
|
|
|
|
$update->addArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL);
|
|
|
|
$update->setCode(function ($input, $output) use ($installer) {
|
2019-11-07 16:35:44 +00:00
|
|
|
$packages = $input->getArgument('packages');
|
|
|
|
$filteredPackages = array_filter($packages, function ($package) {
|
|
|
|
return !in_array($package, array('lock', 'nothing', 'mirrors'), true);
|
|
|
|
});
|
|
|
|
$updateMirrors = $input->getOption('lock') || count($filteredPackages) != count($packages);
|
|
|
|
$packages = $filteredPackages;
|
|
|
|
|
2020-03-27 20:41:49 +00:00
|
|
|
$updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
|
|
|
|
if ($input->getOption('with-all-dependencies')) {
|
2020-03-30 14:21:27 +00:00
|
|
|
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
|
2020-03-27 20:41:49 +00:00
|
|
|
} elseif ($input->getOption('with-dependencies')) {
|
2020-03-30 14:21:27 +00:00
|
|
|
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
|
2020-03-27 20:41:49 +00:00
|
|
|
}
|
|
|
|
|
2020-06-02 09:17:49 +00:00
|
|
|
$ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false);
|
2020-06-01 13:43:24 +00:00
|
|
|
|
2012-05-27 22:11:18 +00:00
|
|
|
$installer
|
2014-04-07 09:10:57 +00:00
|
|
|
->setDevMode(!$input->getOption('no-dev'))
|
2012-05-27 22:11:18 +00:00
|
|
|
->setUpdate(true)
|
2020-04-24 07:07:32 +00:00
|
|
|
->setInstall(!$input->getOption('no-install'))
|
2013-03-21 11:08:58 +00:00
|
|
|
->setDryRun($input->getOption('dry-run'))
|
2019-11-07 16:35:44 +00:00
|
|
|
->setUpdateMirrors($updateMirrors)
|
2020-03-27 20:41:49 +00:00
|
|
|
->setUpdateAllowList($packages)
|
|
|
|
->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
|
2014-12-02 08:18:44 +00:00
|
|
|
->setPreferStable($input->getOption('prefer-stable'))
|
|
|
|
->setPreferLowest($input->getOption('prefer-lowest'))
|
2021-11-11 11:24:12 +00:00
|
|
|
->setPlatformRequirementFilter(PlatformRequirementFilterFactory::fromBoolOrList($ignorePlatformReqs));
|
2012-05-27 22:11:18 +00:00
|
|
|
|
2013-11-22 15:17:02 +00:00
|
|
|
return $installer->run();
|
2012-05-27 22:11:18 +00:00
|
|
|
});
|
2020-06-19 15:56:13 +00:00
|
|
|
$application->add($update);
|
2012-05-27 22:11:18 +00:00
|
|
|
|
2021-12-07 10:03:51 +00:00
|
|
|
if (!Preg::isMatch('{^(install|update)\b}', $run)) {
|
2012-05-27 22:11:18 +00:00
|
|
|
throw new \UnexpectedValueException('The run command only supports install and update');
|
|
|
|
}
|
|
|
|
|
|
|
|
$application->setAutoExit(false);
|
|
|
|
$appOutput = fopen('php://memory', 'w+');
|
2018-09-13 13:23:05 +00:00
|
|
|
$input = new StringInput($run.' -vvv');
|
2017-12-18 16:18:59 +00:00
|
|
|
$input->setInteractive(false);
|
|
|
|
$result = $application->run($input, new StreamOutput($appOutput));
|
2012-05-27 22:11:18 +00:00
|
|
|
fseek($appOutput, 0);
|
2012-05-13 19:40:42 +00:00
|
|
|
|
2016-02-03 17:00:30 +00:00
|
|
|
// Shouldn't check output and results if an exception was expected by this point
|
2016-01-27 12:46:14 +00:00
|
|
|
if (!is_int($expectResult)) {
|
2016-01-27 12:19:08 +00:00
|
|
|
return;
|
2016-01-27 09:04:45 +00:00
|
|
|
}
|
2016-01-27 12:19:08 +00:00
|
|
|
|
2016-02-03 17:00:30 +00:00
|
|
|
$output = str_replace("\r", '', $io->getOutput());
|
2016-01-27 12:46:14 +00:00
|
|
|
$this->assertEquals($expectResult, $result, $output . stream_get_contents($appOutput));
|
2020-04-07 12:13:50 +00:00
|
|
|
if ($expectLock && isset($actualLock)) {
|
2020-09-11 21:51:55 +00:00
|
|
|
unset($actualLock['hash'], $actualLock['content-hash'], $actualLock['_readme'], $actualLock['plugin-api-version']);
|
2012-07-01 16:27:16 +00:00
|
|
|
$this->assertEquals($expectLock, $actualLock);
|
|
|
|
}
|
|
|
|
|
2020-04-20 19:44:15 +00:00
|
|
|
if ($expectInstalled !== null) {
|
|
|
|
$actualInstalled = array();
|
|
|
|
$dumper = new ArrayDumper();
|
|
|
|
|
|
|
|
foreach ($repositoryManager->getLocalRepository()->getCanonicalPackages() as $package) {
|
|
|
|
$package = $dumper->dump($package);
|
|
|
|
unset($package['version_normalized']);
|
|
|
|
$actualInstalled[] = $package;
|
|
|
|
}
|
|
|
|
|
|
|
|
usort($actualInstalled, function ($a, $b) {
|
|
|
|
return strcmp($a['name'], $b['name']);
|
|
|
|
});
|
|
|
|
|
|
|
|
$this->assertSame($expectInstalled, $actualInstalled);
|
|
|
|
}
|
|
|
|
|
2021-03-09 14:49:40 +00:00
|
|
|
/** @var InstallationManagerMock $installationManager */
|
2012-05-13 19:40:42 +00:00
|
|
|
$installationManager = $composer->getInstallationManager();
|
2015-01-27 15:56:39 +00:00
|
|
|
$this->assertSame(rtrim($expect), implode("\n", $installationManager->getTrace()));
|
2012-09-05 18:39:45 +00:00
|
|
|
|
|
|
|
if ($expectOutput) {
|
2021-12-07 10:03:51 +00:00
|
|
|
$output = Preg::replace('{^ - .*?\.ini$}m', '__inilist__', $output);
|
|
|
|
$output = Preg::replace('{(__inilist__\r?\n)+}', "__inilist__\n", $output);
|
2020-01-29 21:47:16 +00:00
|
|
|
|
2016-02-05 12:34:21 +00:00
|
|
|
$this->assertStringMatchesFormat(rtrim($expectOutput), rtrim($output));
|
2012-09-05 18:39:45 +00:00
|
|
|
}
|
2012-05-13 19:40:42 +00:00
|
|
|
}
|
|
|
|
|
2021-11-02 13:32:09 +00:00
|
|
|
public function provideSlowIntegrationTests()
|
2020-10-22 08:14:17 +00:00
|
|
|
{
|
|
|
|
return $this->loadIntegrationTests('installer-slow/');
|
|
|
|
}
|
|
|
|
|
2021-11-02 13:32:09 +00:00
|
|
|
public function provideIntegrationTests()
|
2012-05-13 19:40:42 +00:00
|
|
|
{
|
2020-10-22 08:14:17 +00:00
|
|
|
return $this->loadIntegrationTests('installer/');
|
|
|
|
}
|
|
|
|
|
2021-11-02 13:32:09 +00:00
|
|
|
/**
|
|
|
|
* @param string $path
|
|
|
|
* @return mixed[]
|
|
|
|
*/
|
2020-10-22 08:14:17 +00:00
|
|
|
public function loadIntegrationTests($path)
|
|
|
|
{
|
|
|
|
$fixturesDir = realpath(__DIR__.'/Fixtures/'.$path);
|
2012-05-13 19:40:42 +00:00
|
|
|
$tests = array();
|
|
|
|
|
|
|
|
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($fixturesDir), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
|
2021-12-07 10:03:51 +00:00
|
|
|
if (!Preg::isMatch('/\.test$/', $file)) {
|
2012-05-13 19:40:42 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-10-29 22:08:03 +00:00
|
|
|
try {
|
|
|
|
$testData = $this->readTestFile($file, $fixturesDir);
|
2012-05-13 19:40:42 +00:00
|
|
|
|
2019-10-29 22:08:03 +00:00
|
|
|
$installed = array();
|
|
|
|
$installedDev = array();
|
|
|
|
$lock = array();
|
|
|
|
$expectLock = array();
|
2020-04-20 19:44:15 +00:00
|
|
|
$expectInstalled = null;
|
2019-10-29 22:08:03 +00:00
|
|
|
$expectResult = 0;
|
2012-05-13 19:40:42 +00:00
|
|
|
|
2015-01-27 15:56:39 +00:00
|
|
|
$message = $testData['TEST'];
|
|
|
|
$condition = !empty($testData['CONDITION']) ? $testData['CONDITION'] : null;
|
|
|
|
$composer = JsonFile::parseJson($testData['COMPOSER']);
|
2014-11-20 15:41:29 +00:00
|
|
|
|
2015-01-27 15:56:39 +00:00
|
|
|
if (isset($composer['repositories'])) {
|
|
|
|
foreach ($composer['repositories'] as &$repo) {
|
|
|
|
if ($repo['type'] !== 'composer') {
|
|
|
|
continue;
|
2014-11-20 15:41:29 +00:00
|
|
|
}
|
|
|
|
|
2015-01-27 15:56:39 +00:00
|
|
|
// Change paths like file://foobar to file:///path/to/fixtures
|
2021-12-07 10:03:51 +00:00
|
|
|
if (Preg::isMatch('{^file://[^/]}', $repo['url'])) {
|
2015-01-27 15:56:39 +00:00
|
|
|
$repo['url'] = 'file://' . strtr($fixturesDir, '\\', '/') . '/' . substr($repo['url'], 7);
|
2012-05-28 16:57:59 +00:00
|
|
|
}
|
2015-01-27 15:56:39 +00:00
|
|
|
|
|
|
|
unset($repo);
|
2012-05-13 19:40:42 +00:00
|
|
|
}
|
2015-01-27 15:56:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($testData['LOCK'])) {
|
|
|
|
$lock = JsonFile::parseJson($testData['LOCK']);
|
|
|
|
if (!isset($lock['hash'])) {
|
|
|
|
$lock['hash'] = md5(json_encode($composer));
|
2012-07-01 16:27:16 +00:00
|
|
|
}
|
2012-05-13 19:40:42 +00:00
|
|
|
}
|
2015-01-27 15:56:39 +00:00
|
|
|
if (!empty($testData['INSTALLED'])) {
|
|
|
|
$installed = JsonFile::parseJson($testData['INSTALLED']);
|
|
|
|
}
|
|
|
|
$run = $testData['RUN'];
|
|
|
|
if (!empty($testData['EXPECT-LOCK'])) {
|
2019-10-20 08:51:59 +00:00
|
|
|
if ($testData['EXPECT-LOCK'] === 'false') {
|
|
|
|
$expectLock = false;
|
|
|
|
} else {
|
|
|
|
$expectLock = JsonFile::parseJson($testData['EXPECT-LOCK']);
|
|
|
|
}
|
2015-01-27 15:56:39 +00:00
|
|
|
}
|
2020-04-20 19:44:15 +00:00
|
|
|
if (!empty($testData['EXPECT-INSTALLED'])) {
|
|
|
|
$expectInstalled = JsonFile::parseJson($testData['EXPECT-INSTALLED']);
|
|
|
|
}
|
2015-01-27 15:56:39 +00:00
|
|
|
$expectOutput = isset($testData['EXPECT-OUTPUT']) ? $testData['EXPECT-OUTPUT'] : null;
|
2020-10-01 14:42:02 +00:00
|
|
|
$expectOutputOptimized = isset($testData['EXPECT-OUTPUT-OPTIMIZED']) ? $testData['EXPECT-OUTPUT-OPTIMIZED'] : null;
|
2015-01-27 15:56:39 +00:00
|
|
|
$expect = $testData['EXPECT'];
|
2016-01-27 12:46:14 +00:00
|
|
|
if (!empty($testData['EXPECT-EXCEPTION'])) {
|
|
|
|
$expectResult = $testData['EXPECT-EXCEPTION'];
|
|
|
|
if (!empty($testData['EXPECT-EXIT-CODE'])) {
|
|
|
|
throw new \LogicException('EXPECT-EXCEPTION and EXPECT-EXIT-CODE are mutually exclusive');
|
|
|
|
}
|
|
|
|
} elseif (!empty($testData['EXPECT-EXIT-CODE'])) {
|
|
|
|
$expectResult = (int) $testData['EXPECT-EXIT-CODE'];
|
|
|
|
} else {
|
|
|
|
$expectResult = 0;
|
|
|
|
}
|
2015-01-27 15:56:39 +00:00
|
|
|
} catch (\Exception $e) {
|
|
|
|
die(sprintf('Test "%s" is not valid: '.$e->getMessage(), str_replace($fixturesDir.'/', '', $file)));
|
2012-05-13 19:40:42 +00:00
|
|
|
}
|
|
|
|
|
2020-10-01 14:42:02 +00:00
|
|
|
$tests[basename($file)] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $run, $expectLock, $expectInstalled, $expectOutput, $expectOutputOptimized, $expect, $expectResult);
|
2012-05-13 19:40:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $tests;
|
|
|
|
}
|
2015-01-27 15:56:39 +00:00
|
|
|
|
2021-11-02 13:32:09 +00:00
|
|
|
/**
|
|
|
|
* @param string $fixturesDir
|
|
|
|
* @return mixed[]
|
|
|
|
*/
|
2015-01-27 15:56:39 +00:00
|
|
|
protected function readTestFile(\SplFileInfo $file, $fixturesDir)
|
|
|
|
{
|
2021-12-07 10:03:51 +00:00
|
|
|
$tokens = Preg::split('#(?:^|\n*)--([A-Z-]+)--\n#', file_get_contents($file->getRealPath()), -1, PREG_SPLIT_DELIM_CAPTURE);
|
2015-01-27 15:56:39 +00:00
|
|
|
|
|
|
|
$sectionInfo = array(
|
|
|
|
'TEST' => true,
|
|
|
|
'CONDITION' => false,
|
|
|
|
'COMPOSER' => true,
|
|
|
|
'LOCK' => false,
|
2017-03-08 14:07:29 +00:00
|
|
|
'INSTALLED' => false,
|
2015-01-27 15:56:39 +00:00
|
|
|
'RUN' => true,
|
|
|
|
'EXPECT-LOCK' => false,
|
2020-04-20 19:44:15 +00:00
|
|
|
'EXPECT-INSTALLED' => false,
|
2015-01-27 15:56:39 +00:00
|
|
|
'EXPECT-OUTPUT' => false,
|
2020-10-01 14:42:02 +00:00
|
|
|
'EXPECT-OUTPUT-OPTIMIZED' => false,
|
2015-01-27 15:56:39 +00:00
|
|
|
'EXPECT-EXIT-CODE' => false,
|
2016-01-27 12:46:14 +00:00
|
|
|
'EXPECT-EXCEPTION' => false,
|
2015-01-27 15:56:39 +00:00
|
|
|
'EXPECT' => true,
|
|
|
|
);
|
|
|
|
|
|
|
|
$section = null;
|
2020-04-07 12:13:50 +00:00
|
|
|
$data = array();
|
2015-02-24 14:22:54 +00:00
|
|
|
foreach ($tokens as $i => $token) {
|
2015-01-27 15:56:39 +00:00
|
|
|
if (null === $section && empty($token)) {
|
|
|
|
continue; // skip leading blank
|
|
|
|
}
|
|
|
|
|
|
|
|
if (null === $section) {
|
|
|
|
if (!isset($sectionInfo[$token])) {
|
|
|
|
throw new \RuntimeException(sprintf(
|
|
|
|
'The test file "%s" must not contain a section named "%s".',
|
|
|
|
str_replace($fixturesDir.'/', '', $file),
|
|
|
|
$token
|
|
|
|
));
|
|
|
|
}
|
|
|
|
$section = $token;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$sectionData = $token;
|
|
|
|
|
|
|
|
$data[$section] = $sectionData;
|
|
|
|
$section = $sectionData = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($sectionInfo as $section => $required) {
|
|
|
|
if ($required && !isset($data[$section])) {
|
|
|
|
throw new \RuntimeException(sprintf(
|
|
|
|
'The test file "%s" must have a section named "%s".',
|
|
|
|
str_replace($fixturesDir.'/', '', $file),
|
|
|
|
$section
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $data;
|
|
|
|
}
|
2012-04-27 09:42:58 +00:00
|
|
|
}
|