From 9f6aad0e79a576288ee2b6dc7db975c7984f5ec9 Mon Sep 17 00:00:00 2001 From: webimpress Date: Fri, 10 Nov 2017 10:59:58 +0000 Subject: [PATCH 1/7] Added functionality to choose one dependency from the list --- README.md | 11 +- src/Plugin.php | 131 +++++++++-- test/PluginTest.php | 562 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 579 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index 07671aa..6dcd251 100644 --- a/README.md +++ b/README.md @@ -29,11 +29,18 @@ you'd like to install with your library: "dependency": [ "package/to-require", ... - ] + ], + "dependency-or": { + "Question": [ + "package/to-choose", + "package/or-this", + ... + ] + } }, "require": { "php": "^5.6 || ^7.0", - "webimpress/composer-extra-dependency": "^0.1 || ^1.0", + "webimpress/composer-extra-dependency": "^0.3 || ^1.0", ... } ... diff --git a/src/Plugin.php b/src/Plugin.php index e8c31b1..208ad11 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -21,6 +21,7 @@ use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryFactory; +use RuntimeException; class Plugin implements PluginInterface, EventSubscriberInterface { @@ -63,7 +64,7 @@ public function activate(Composer $composer, IOInterface $io) $installedPackages = $this->composer->getRepositoryManager()->getLocalRepository()->getPackages(); foreach ($installedPackages as $package) { - $this->installedPackages[$package->getName()] = $package->getPrettyVersion(); + $this->installedPackages[strtolower($package->getName())] = $package->getPrettyVersion(); } } @@ -85,13 +86,29 @@ public function onPostPackage(PackageEvent $event) } else { $package = $operation->getTargetPackage(); } - $extra = $this->getExtraMetadata($package->getExtra()); - if (empty($extra)) { - // Package does not define anything of interest; do nothing. - return; + + $extra = $package->getExtra(); + $packages = $this->andDependencies($extra) + $this->orDependencies($extra); + if ($packages) { + $this->updateComposerJson($packages); + + $rootPackage = $this->updateRootPackage($this->composer->getPackage(), $packages); + $this->runInstaller($rootPackage, array_keys($packages)); + } + } + + private function andDependencies(array $extra) + { + $deps = isset($extra['dependency']) && is_array($extra['dependency']) + ? $extra['dependency'] + : []; + + if (! $deps) { + // No defined any packages to install + return []; } - $packages = array_flip($extra); + $packages = array_flip($deps); foreach ($packages as $package => &$constraint) { if ($this->hasPackage($package)) { @@ -99,22 +116,53 @@ public function onPostPackage(PackageEvent $event) continue; } + // Check if package is currently installed and use installed version. + if ($constraint = $this->getInstalledPackageConstraint($package)) { + continue; + } + + // Package is not installed, then prompt user for the version. $constraint = $this->promptForPackageVersion($package); } - if ($packages) { - $this->updateComposerJson($packages); - - $rootPackage = $this->updateRootPackage($this->composer->getPackage(), $packages); - $this->runInstaller($rootPackage, array_keys($packages)); - } + return $packages; } - private function getExtraMetadata(array $extra) + private function orDependencies(array $extra) { - return isset($extra['dependency']) && is_array($extra['dependency']) - ? $extra['dependency'] + $deps = isset($extra['dependency-or']) && is_array($extra['dependency-or']) + ? $extra['dependency-or'] : []; + + if (! $deps) { + // No any dependencies to choose defined in the package. + return []; + } + + $packages = []; + foreach ($deps as $question => $options) { + if (! is_array($options) || count($options) < 2) { + throw new RuntimeException('You must provide at least two optional dependencies.'); + } + + foreach ($options as $package) { + if ($this->hasPackage($package)) { + // Package from this group has been found in root composer, skipping. + continue 2; + } + + // Check if package is currently installed, if so, use installed constraint and skip question. + if ($constraint = $this->getInstalledPackageConstraint($package)) { + $packages[$package] = $constraint; + continue 2; + } + } + + $package = $this->promptForPackageSelection($question, $options); + $packages[$package] = $this->promptForPackageVersion($package); + } + + return $packages; } private function updateRootPackage(RootPackageInterface $rootPackage, array $packages) @@ -157,21 +205,54 @@ private function runInstaller(RootPackageInterface $rootPackage, array $packages return $installer->run(); } - private function promptForPackageVersion($name) + private function getInstalledPackageConstraint($package) { + $lower = strtolower($package); + // Package is currently installed. Add it to root composer.json - if (isset($this->installedPackages[$name])) { - $this->io->write(sprintf( - 'Added package %s to composer.json with constraint %s;' - . ' to upgrade, run composer require %s:VERSION', - $name, - '^' . $this->installedPackages[$name], - $name - )); + if (! isset($this->installedPackages[$lower])) { + return null; + } - return '^' . $this->installedPackages[$name]; + $constraint = '^' . $this->installedPackages[$lower]; + $this->io->write(sprintf( + 'Added package %s to composer.json with constraint %s;' + . ' to upgrade, run composer require %s:VERSION', + $package, + $constraint, + $package + )); + + return $constraint; + } + + private function promptForPackageSelection($question, array $packages) + { + $ask = [sprintf('%s', $question)]; + foreach ($packages as $i => $name) { + $ask[] = sprintf('[%d] %s', $i + 1, $name); } + do { + $package = $this->io->askAndValidate( + $ask, + function ($input) use ($packages) { + $input = is_numeric($input) ? (int) trim($input) : 0; + + if (isset($packages[$input - 1])) { + return $packages[$input - 1]; + } + + return null; + } + ); + } while (! $package); + + return $package; + } + + private function promptForPackageVersion($name) + { $constraint = $this->io->askAndValidate( sprintf( 'Enter the version of %s to require (or leave blank to use the latest version): ', diff --git a/test/PluginTest.php b/test/PluginTest.php index fff6b98..261d095 100644 --- a/test/PluginTest.php +++ b/test/PluginTest.php @@ -20,11 +20,13 @@ use Composer\Repository\RepositoryManager; use Composer\Repository\WritableRepositoryInterface; use org\bovigo\vfs\vfsStream; +use PHPUnit\Framework\Assert; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; use ReflectionMethod; use ReflectionProperty; +use RuntimeException; use Webimpress\ComposerExtraDependency\Plugin; class PluginTest extends TestCase @@ -291,30 +293,12 @@ public function testInstallSingleDependencyOnPackageUpdate() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn([]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function ($arguments) { - if (! is_array($arguments)) { + $rootPackage->setRequires(Argument::that(function (array $arguments) { + if (count($arguments) !== 1) { return false; } - if (! isset($arguments['extra-dependency-foo'])) { - return false; - } - - $argument = $arguments['extra-dependency-foo']; - - if (! $argument instanceof Link) { - return false; - } - - if ($argument->getTarget() !== 'extra-dependency-foo') { - return false; - } - - if ($argument->getConstraint()->getPrettyString() !== '17.0.1-dev') { - return false; - } - - if ($argument->getDescription() !== 'requires') { + if (! $this->assertSetRequiresArgument('extra-dependency-foo', '17.0.1-dev', $arguments)) { return false; } @@ -328,7 +312,21 @@ public function testInstallSingleDependencyOnPackageUpdate() $this->io->askAndValidate( 'Enter the version of extra-dependency-foo to require' . ' (or leave blank to use the latest version): ', - Argument::type('callable') + Argument::that(function ($arg) { + if (! is_callable($arg)) { + return false; + } + + Assert::assertFalse($arg(0)); + Assert::assertFalse($arg('0')); + Assert::assertFalse($arg('')); + Assert::assertFalse($arg(' ')); + Assert::assertSame('1', $arg(' 1 ')); + Assert::assertSame('1', $arg('1')); + Assert::assertSame('0.*', $arg('0.*')); + + return true; + }) )->willReturn('17.0.1-dev'); $this->io->write(' Updating composer.json')->shouldBeCalled(); @@ -366,30 +364,12 @@ public function testInstallSingleDependencyOnPackageInstall() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn([]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function ($arguments) { - if (! is_array($arguments)) { - return false; - } - - if (! isset($arguments['extra-dependency-foo'])) { - return false; - } - - $argument = $arguments['extra-dependency-foo']; - - if (! $argument instanceof Link) { - return false; - } - - if ($argument->getTarget() !== 'extra-dependency-foo') { - return false; - } - - if ($argument->getConstraint()->getPrettyString() !== '17.0.1-dev') { + $rootPackage->setRequires(Argument::that(function (array $arguments) { + if (count($arguments) !== 1) { return false; } - if ($argument->getDescription() !== 'requires') { + if (! $this->assertSetRequiresArgument('extra-dependency-foo', '17.0.1-dev', $arguments)) { return false; } @@ -445,12 +425,12 @@ public function testInstallOneDependenciesWhenOneIsAlreadyInstalled() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn(['extra-dependency-bar' => $link->reveal()]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function ($arguments) { - if (! is_array($arguments)) { + $rootPackage->setRequires(Argument::that(function (array $arguments) { + if (count($arguments) !== 2) { return false; } - if (! isset($arguments['extra-dependency-foo'])) { + if (! $this->assertSetRequiresArgument('extra-dependency-foo', '17.0.1-dev', $arguments)) { return false; } @@ -458,24 +438,6 @@ public function testInstallOneDependenciesWhenOneIsAlreadyInstalled() return false; } - $argument = $arguments['extra-dependency-foo']; - - if (! $argument instanceof Link) { - return false; - } - - if ($argument->getTarget() !== 'extra-dependency-foo') { - return false; - } - - if ($argument->getConstraint()->getPrettyString() !== '17.0.1-dev') { - return false; - } - - if ($argument->getDescription() !== 'requires') { - return false; - } - return true; }))->shouldBeCalled(); @@ -529,30 +491,12 @@ public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersion() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn([]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function ($arguments) { - if (! is_array($arguments)) { + $rootPackage->setRequires(Argument::that(function (array $arguments) { + if (count($arguments) !== 1) { return false; } - if (! isset($arguments['extra-dependency-foo'])) { - return false; - } - - $argument = $arguments['extra-dependency-foo']; - - if (! $argument instanceof Link) { - return false; - } - - if ($argument->getTarget() !== 'extra-dependency-foo') { - return false; - } - - if ($argument->getConstraint()->getPrettyString() !== '13.4.2') { - return false; - } - - if ($argument->getDescription() !== 'requires') { + if (! $this->assertSetRequiresArgument('extra-dependency-foo', '13.4.2', $arguments)) { return false; } @@ -683,30 +627,208 @@ public function testUpdateComposerWithCurrentlyInstalledVersion() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn([]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function ($arguments) { - if (! is_array($arguments)) { + $rootPackage->setRequires(Argument::that(function (array $arguments) { + if (count($arguments) !== 1) { return false; } - if (! isset($arguments['extra-dependency-foo'])) { + if (! $this->assertSetRequiresArgument('extra-dependency-foo', '^0.5.1', $arguments)) { return false; } - $argument = $arguments['extra-dependency-foo']; + return true; + }))->shouldBeCalled(); + $rootPackage->getMinimumStability()->willReturn('stable'); + + $this->composer->getPackage()->willReturn($rootPackage); + $this->composer->getConfig()->willReturn($config->reveal()); + + $this->io->isInteractive()->willReturn(true); + $this->io + ->write( + 'Added package extra-dependency-foo to composer.json with constraint' + . ' ^0.5.1; to upgrade, run composer require extra-dependency-foo:VERSION' + ) + ->shouldBeCalled(); + $this->io->write(' Updating composer.json')->shouldBeCalled(); + $this->io->write('Updating root package')->shouldBeCalled(); + $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); + + $this->setUpComposerInstaller(['extra-dependency-foo']); + $this->setUpComposerJson(); + + $this->assertNull($this->plugin->onPostPackage($event->reveal())); + + $json = file_get_contents(vfsStream::url('project/composer.json')); + $composer = json_decode($json, true); + $this->assertTrue(isset($composer['require']['extra-dependency-foo'])); + $this->assertSame('^0.5.1', $composer['require']['extra-dependency-foo']); + } + + public function testDependencyOrChoosePackageToInstall() + { + /** @var PackageInterface|ObjectProphecy $package */ + $package = $this->prophesize(PackageInterface::class); + $package->getName()->willReturn('some/component'); + $package->getExtra()->willReturn([ + 'dependency-or' => [ + 'My question foo bar baz' => [ + 'extra-dependency-foo', + 'extra-dependency-bar', + ], + ], + ]); + + $operation = $this->prophesize(InstallOperation::class); + $operation->getPackage()->willReturn($package->reveal()); + + $event = $this->prophesize(PackageEvent::class); + $event->isDevMode()->willReturn(true); + $event->getOperation()->willReturn($operation->reveal()); + + $config = $this->prophesize(Config::class); + $config->get('sort-packages')->willReturn(true); + $config->get(Argument::any())->willReturn(null); - if (! $argument instanceof Link) { + $rootPackage = $this->prophesize(RootPackageInterface::class); + $rootPackage->getRequires()->willReturn([]); + $rootPackage->getDevRequires()->willReturn([]); + $rootPackage->setRequires(Argument::that(function (array $arguments) { + if (count($arguments) !== 1) { return false; } - if ($argument->getTarget() !== 'extra-dependency-foo') { + if (! $this->assertSetRequiresArgument('extra-dependency-bar', '13.4.2', $arguments)) { return false; } - if ($argument->getConstraint()->getPrettyString() !== '^0.5.1') { + return true; + }))->shouldBeCalled(); + $rootPackage->getMinimumStability()->willReturn('stable'); + + $this->composer->getPackage()->willReturn($rootPackage); + $this->composer->getConfig()->willReturn($config->reveal()); + + $this->io->isInteractive()->willReturn(true); + $this->io + ->askAndValidate( + Argument::that(function ($arg) { + if (! is_array($arg)) { + return false; + } + + Assert::assertCount(3, $arg); + Assert::assertSame('My question foo bar baz', $arg[0]); + Assert::assertSame('[1] extra-dependency-foo', $arg[1]); + Assert::assertSame('[2] extra-dependency-bar', $arg[2]); + + return true; + }), + Argument::that(function ($arg) { + if (! is_callable($arg)) { + return false; + } + + Assert::assertSame('extra-dependency-foo', $arg(1)); + Assert::assertSame('extra-dependency-foo', $arg('1')); + Assert::assertSame('extra-dependency-foo', $arg(' 1')); + Assert::assertSame('extra-dependency-foo', $arg('1.0')); + Assert::assertSame('extra-dependency-bar', $arg(2)); + Assert::assertSame('extra-dependency-bar', $arg('2')); + Assert::assertSame('extra-dependency-bar', $arg(' 2')); + Assert::assertSame('extra-dependency-bar', $arg('2.2')); + Assert::assertNull($arg('')); + Assert::assertNull($arg(' ')); + Assert::assertNull($arg('a')); + Assert::assertNull($arg('1a')); + Assert::assertNull($arg(' a')); + Assert::assertNull($arg(0)); + Assert::assertNull($arg(3)); + + return true; + }) + ) + ->willReturn('', 'extra-dependency-bar') + ->shouldBeCalledTimes(2); + $this->io + ->askAndValidate( + 'Enter the version of extra-dependency-bar to require' + . ' (or leave blank to use the latest version): ', + Argument::type('callable') + ) + ->willReturn(false) + ->shouldBeCalledTimes(1); + + $this->io->write(' Updating composer.json')->shouldBeCalled(); + $this->io->write('Updating root package')->shouldBeCalled(); + $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); + $this->io->write('Using version 13.4.2 for extra-dependency-bar')->shouldBeCalled(); + + $this->setUpComposerInstaller(['extra-dependency-bar']); + $this->setUpComposerJson(); + + $package = $this->prophesize(PackageInterface::class); + + $versionSelector = $this->prophesize(VersionSelector::class); + $versionSelector->findBestCandidate('extra-dependency-bar', null, null, 'stable') + ->willReturn($package->reveal()) + ->shouldBeCalled(); + $versionSelector->findRecommendedRequireVersion($package->reveal()) + ->willReturn('13.4.2') + ->shouldBeCalled(); + + $this->setUpVersionSelector($versionSelector->reveal()); + $this->setUpPool(); + + $this->assertNull($this->plugin->onPostPackage($event->reveal())); + + $json = file_get_contents(vfsStream::url('project/composer.json')); + $composer = json_decode($json, true); + $this->assertTrue(isset($composer['require']['extra-dependency-bar'])); + $this->assertSame('13.4.2', $composer['require']['extra-dependency-bar']); + } + + public function testDependencyOrOnePackageIsAlreadyInstalledAndShouldBeAddedIntoRootComposer() + { + $installedPackage = $this->prophesize(PackageInterface::class); + $installedPackage->getName()->willReturn('extra-dependency-baz'); + $installedPackage->getPrettyVersion()->willReturn('3.7.1'); + + $this->localRepository->getPackages()->willReturn([$installedPackage->reveal()]); + $this->plugin->activate($this->composer->reveal(), $this->io->reveal()); + + /** @var PackageInterface|ObjectProphecy $package */ + $package = $this->prophesize(PackageInterface::class); + $package->getName()->willReturn('some/component'); + $package->getExtra()->willReturn([ + 'dependency-or' => [ + 'Choose something' => [ + 'extra-dependency-bar', + 'extra-dependency-baz', + ], + ], + ]); + + $operation = $this->prophesize(InstallOperation::class); + $operation->getPackage()->willReturn($package->reveal()); + + $event = $this->prophesize(PackageEvent::class); + $event->isDevMode()->willReturn(true); + $event->getOperation()->willReturn($operation->reveal()); + + $config = $this->prophesize(Config::class); + $config->get('sort-packages')->willReturn(true); + $config->get(Argument::any())->willReturn(null); + + $rootPackage = $this->prophesize(RootPackageInterface::class); + $rootPackage->getRequires()->willReturn([]); + $rootPackage->getDevRequires()->willReturn([]); + $rootPackage->setRequires(Argument::that(function (array $arguments) { + if (count($arguments) !== 1) { return false; } - if ($argument->getDescription() !== 'requires') { + if (! $this->assertSetRequiresArgument('extra-dependency-baz', '^3.7.1', $arguments)) { return false; } @@ -720,23 +842,267 @@ public function testUpdateComposerWithCurrentlyInstalledVersion() $this->io->isInteractive()->willReturn(true); $this->io ->write( - 'Added package extra-dependency-foo to composer.json with constraint' - . ' ^0.5.1; to upgrade, run composer require extra-dependency-foo:VERSION' + 'Added package extra-dependency-baz to composer.json with constraint' + . ' ^3.7.1; to upgrade, run composer require extra-dependency-baz:VERSION' ) ->shouldBeCalled(); + $this->io + ->askAndValidate(Argument::any(), Argument::any()) + ->shouldNotBeCalled(); + $this->io->write(' Updating composer.json')->shouldBeCalled(); $this->io->write('Updating root package')->shouldBeCalled(); $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); - $this->setUpComposerInstaller(['extra-dependency-foo']); + $this->setUpComposerInstaller(['extra-dependency-baz']); $this->setUpComposerJson(); + $this->setUpPool(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); $json = file_get_contents(vfsStream::url('project/composer.json')); $composer = json_decode($json, true); - $this->assertTrue(isset($composer['require']['extra-dependency-foo'])); - $this->assertSame('^0.5.1', $composer['require']['extra-dependency-foo']); + $this->assertTrue(isset($composer['require']['extra-dependency-baz'])); + $this->assertSame('^3.7.1', $composer['require']['extra-dependency-baz']); + } + + public function testDependencyOrOnePackageIsAlreadyInRootComposer() + { + /** @var PackageInterface|ObjectProphecy $package */ + $package = $this->prophesize(PackageInterface::class); + $package->getName()->willReturn('some/component'); + $package->getExtra()->willReturn([ + 'dependency-or' => [ + 'Choose something' => [ + 'extra-dependency-foo', + 'extra-dependency-baz', + ], + ], + ]); + + $operation = $this->prophesize(InstallOperation::class); + $operation->getPackage()->willReturn($package->reveal()); + + $event = $this->prophesize(PackageEvent::class); + $event->isDevMode()->willReturn(true); + $event->getOperation()->willReturn($operation->reveal()); + + $config = $this->prophesize(Config::class); + $config->get('sort-packages')->willReturn(true); + $config->get(Argument::any())->willReturn(null); + + $link = $this->prophesize(Link::class); + $link->getTarget()->willReturn('extra-dependency-bar'); + + $rootPackage = $this->prophesize(RootPackageInterface::class); + $rootPackage->getRequires()->willReturn(['extra-dependency-foo' => $link]); + $rootPackage->getDevRequires()->willReturn([]); + $rootPackage->setRequires(Argument::any())->shouldNotBeCalled(); + $rootPackage->getMinimumStability()->willReturn('stable'); + + $this->composer->getPackage()->willReturn($rootPackage); + $this->composer->getConfig()->willReturn($config->reveal()); + + $this->io->isInteractive()->willReturn(true)->shouldBeCalledTimes(1); + $this->io + ->askAndValidate(Argument::any(), Argument::any()) + ->shouldNotBeCalled(); + + $this->assertNull($this->plugin->onPostPackage($event->reveal())); + } + + public function testDependencyOrWrongDefinitionThrowsException() + { + /** @var PackageInterface|ObjectProphecy $package */ + $package = $this->prophesize(PackageInterface::class); + $package->getName()->willReturn('some/component'); + $package->getExtra()->willReturn([ + 'dependency-or' => [ + 'extra-dependency-foo', + 'extra-dependency-baz', + ], + ]); + + $operation = $this->prophesize(InstallOperation::class); + $operation->getPackage()->willReturn($package->reveal()); + + $event = $this->prophesize(PackageEvent::class); + $event->isDevMode()->willReturn(true); + $event->getOperation()->willReturn($operation->reveal()); + + $config = $this->prophesize(Config::class); + $config->get('sort-packages')->willReturn(true); + $config->get(Argument::any())->willReturn(null); + + $link = $this->prophesize(Link::class); + $link->getTarget()->willReturn('extra-dependency-bar'); + + $rootPackage = $this->prophesize(RootPackageInterface::class); + $rootPackage->getRequires()->willReturn(['extra-dependency-foo' => $link]); + $rootPackage->getDevRequires()->willReturn([]); + $rootPackage->setRequires(Argument::any())->shouldNotBeCalled(); + $rootPackage->getMinimumStability()->willReturn('stable'); + + $this->composer->getPackage()->willReturn($rootPackage); + $this->composer->getConfig()->willReturn($config->reveal()); + + $this->io->isInteractive()->willReturn(true)->shouldBeCalledTimes(1); + $this->io + ->askAndValidate(Argument::any(), Argument::any()) + ->shouldNotBeCalled(); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('You must provide at least two optional dependencies.'); + $this->assertNull($this->plugin->onPostPackage($event->reveal())); + } + + public function testIntegrationHandleDependencyAndDependencyOr() + { + /** @var PackageInterface|ObjectProphecy $package */ + $package = $this->prophesize(PackageInterface::class); + $package->getName()->willReturn('some/component'); + $package->getExtra()->willReturn([ + 'dependency' => [ + 'extra-package-required', + ], + 'dependency-or' => [ + 'Choose something' => [ + 'extra-choose-one', + 'extra-choose-two', + 'extra-choose-three', + ], + ], + ]); + + $operation = $this->prophesize(InstallOperation::class); + $operation->getPackage()->willReturn($package->reveal()); + + $event = $this->prophesize(PackageEvent::class); + $event->isDevMode()->willReturn(true); + $event->getOperation()->willReturn($operation->reveal()); + + $config = $this->prophesize(Config::class); + $config->get('sort-packages')->willReturn(true); + $config->get(Argument::any())->willReturn(null); + + $rootPackage = $this->prophesize(RootPackageInterface::class); + $rootPackage->getRequires()->willReturn([]); + $rootPackage->getDevRequires()->willReturn([]); + $rootPackage->setRequires(Argument::that(function (array $arguments) { + if (count($arguments) !== 2) { + return false; + } + + if (! $this->assertSetRequiresArgument('extra-package-required', '3.9.1', $arguments)) { + return false; + } + + if (! $this->assertSetRequiresArgument('extra-choose-two', '2.1.5', $arguments)) { + return false; + } + + return true; + }))->shouldBeCalled(); + $rootPackage->getMinimumStability()->willReturn('stable'); + + $this->composer->getPackage()->willReturn($rootPackage); + $this->composer->getConfig()->willReturn($config->reveal()); + + $this->io->isInteractive()->willReturn(true); + $this->io + ->askAndValidate( + Argument::that(function ($arg) { + if (! is_array($arg)) { + return false; + } + + Assert::assertCount(4, $arg); + Assert::assertSame('Choose something', $arg[0]); + Assert::assertSame('[1] extra-choose-one', $arg[1]); + Assert::assertSame('[2] extra-choose-two', $arg[2]); + Assert::assertSame('[3] extra-choose-three', $arg[3]); + + return true; + }), + Argument::type('callable') + ) + ->willReturn('extra-choose-two') + ->shouldBeCalledTimes(1); + $this->io + ->askAndValidate( + 'Enter the version of extra-package-required to require' + . ' (or leave blank to use the latest version): ', + Argument::type('callable') + ) + ->willReturn(false) + ->shouldBeCalledTimes(1); + $this->io + ->askAndValidate( + 'Enter the version of extra-choose-two to require' + . ' (or leave blank to use the latest version): ', + Argument::type('callable') + ) + ->willReturn(false) + ->shouldBeCalledTimes(1); + + $this->io->write(' Updating composer.json')->shouldBeCalled(); + $this->io->write('Updating root package')->shouldBeCalled(); + $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); + $this->io->write('Using version 3.9.1 for extra-package-required')->shouldBeCalled(); + $this->io->write('Using version 2.1.5 for extra-choose-two')->shouldBeCalled(); + + $this->setUpComposerInstaller(['extra-package-required', 'extra-choose-two']); + $this->setUpComposerJson(); + + $versionSelector = $this->prophesize(VersionSelector::class); + $versionSelector->findBestCandidate('extra-package-required', null, null, 'stable') + ->willReturn($package->reveal()) + ->shouldBeCalledTimes(1); + $versionSelector->findBestCandidate('extra-choose-two', null, null, 'stable') + ->willReturn($package->reveal()) + ->shouldBeCalledTimes(1); + $versionSelector->findRecommendedRequireVersion($package->reveal()) + ->willReturn('3.9.1', '2.1.5') + ->shouldBeCalledTimes(2); + + $this->setUpVersionSelector($versionSelector->reveal()); + $this->setUpPool(); + + $this->assertNull($this->plugin->onPostPackage($event->reveal())); + + $json = file_get_contents(vfsStream::url('project/composer.json')); + $composer = json_decode($json, true); + $this->assertTrue(isset($composer['require']['extra-package-required'])); + $this->assertSame('3.9.1', $composer['require']['extra-package-required']); + $this->assertTrue(isset($composer['require']['extra-choose-two'])); + $this->assertSame('2.1.5', $composer['require']['extra-choose-two']); + } + + private function assertSetRequiresArgument($name, $version, array $arguments) + { + if (! isset($arguments[$name])) { + return false; + } + + $argument = $arguments[$name]; + + if (! $argument instanceof Link) { + return false; + } + + if ($argument->getTarget() !== $name) { + return false; + } + + if ($argument->getConstraint()->getPrettyString() !== $version) { + return false; + } + + if ($argument->getDescription() !== 'requires') { + return false; + } + + return true; } public function testComposerInstallerFactory() From e239492152348487646b6c142d44e1c19b1afabd Mon Sep 17 00:00:00 2001 From: webimpress Date: Fri, 10 Nov 2017 11:30:18 +0000 Subject: [PATCH 2/7] Added new lines and indents when prompt for package choice --- src/Plugin.php | 5 +++-- test/PluginTest.php | 20 +++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Plugin.php b/src/Plugin.php index 208ad11..9469d90 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -228,10 +228,11 @@ private function getInstalledPackageConstraint($package) private function promptForPackageSelection($question, array $packages) { - $ask = [sprintf('%s', $question)]; + $ask = [sprintf('%s' . "\n", $question)]; foreach ($packages as $i => $name) { - $ask[] = sprintf('[%d] %s', $i + 1, $name); + $ask[] = sprintf(' [%d] %s' . "\n", $i + 1, $name); } + $ask[] = ' Make your selection: '; do { $package = $this->io->askAndValidate( diff --git a/test/PluginTest.php b/test/PluginTest.php index 261d095..9352c3e 100644 --- a/test/PluginTest.php +++ b/test/PluginTest.php @@ -717,10 +717,11 @@ public function testDependencyOrChoosePackageToInstall() return false; } - Assert::assertCount(3, $arg); - Assert::assertSame('My question foo bar baz', $arg[0]); - Assert::assertSame('[1] extra-dependency-foo', $arg[1]); - Assert::assertSame('[2] extra-dependency-bar', $arg[2]); + Assert::assertCount(4, $arg); + Assert::assertSame('My question foo bar baz' . "\n", $arg[0]); + Assert::assertSame(' [1] extra-dependency-foo' . "\n", $arg[1]); + Assert::assertSame(' [2] extra-dependency-bar' . "\n", $arg[2]); + Assert::assertSame(' Make your selection: ', $arg[3]); return true; }), @@ -1016,11 +1017,12 @@ public function testIntegrationHandleDependencyAndDependencyOr() return false; } - Assert::assertCount(4, $arg); - Assert::assertSame('Choose something', $arg[0]); - Assert::assertSame('[1] extra-choose-one', $arg[1]); - Assert::assertSame('[2] extra-choose-two', $arg[2]); - Assert::assertSame('[3] extra-choose-three', $arg[3]); + Assert::assertCount(5, $arg); + Assert::assertSame('Choose something' . "\n", $arg[0]); + Assert::assertSame(' [1] extra-choose-one' . "\n", $arg[1]); + Assert::assertSame(' [2] extra-choose-two' . "\n", $arg[2]); + Assert::assertSame(' [3] extra-choose-three' . "\n", $arg[3]); + Assert::assertSame(' Make your selection: ', $arg[4]); return true; }), From b23c7ebf9fcf8be737a799122fc9f928fecd965c Mon Sep 17 00:00:00 2001 From: webimpress Date: Fri, 10 Nov 2017 13:40:50 +0000 Subject: [PATCH 3/7] Install extra dependencies only on post command not after every package Before we run update composer internally after every package which requires extra dependencies. Now we run it only once, at the end - on post command (install/update). --- src/Plugin.php | 34 ++++++++++++++++++++++++++++------ test/PluginTest.php | 26 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/Plugin.php b/src/Plugin.php index 9469d90..9a18921 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -21,6 +21,7 @@ use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryFactory; +use Composer\Script\Event; use RuntimeException; class Plugin implements PluginInterface, EventSubscriberInterface @@ -49,11 +50,16 @@ class Plugin implements PluginInterface, EventSubscriberInterface /** @var CompositeRepository */ private $repos; + /** @var string[] */ + private $packagesToInstall = []; + public static function getSubscribedEvents() { return [ 'post-package-install' => 'onPostPackage', 'post-package-update' => 'onPostPackage', + 'post-install-cmd' => 'onPostCommand', + 'post-update-cmd' => 'onPostCommand', ]; } @@ -68,6 +74,26 @@ public function activate(Composer $composer, IOInterface $io) } } + public function onPostCommand(Event $event) + { + if (! $event->isDevMode()) { + // Do nothing in production mode. + return; + } + + if (! $this->io->isInteractive()) { + // Do nothing in no-interactive mode + return; + } + + if ($this->packagesToInstall) { + $this->updateComposerJson($this->packagesToInstall); + + $rootPackage = $this->updateRootPackage($this->composer->getPackage(), $this->packagesToInstall); + $this->runInstaller($rootPackage, array_keys($this->packagesToInstall)); + } + } + public function onPostPackage(PackageEvent $event) { if (! $event->isDevMode()) { @@ -88,13 +114,9 @@ public function onPostPackage(PackageEvent $event) } $extra = $package->getExtra(); - $packages = $this->andDependencies($extra) + $this->orDependencies($extra); - if ($packages) { - $this->updateComposerJson($packages); - $rootPackage = $this->updateRootPackage($this->composer->getPackage(), $packages); - $this->runInstaller($rootPackage, array_keys($packages)); - } + $this->packagesToInstall += $this->andDependencies($extra); + $this->packagesToInstall += $this->orDependencies($extra); } private function andDependencies(array $extra) diff --git a/test/PluginTest.php b/test/PluginTest.php index 9352c3e..1f45aef 100644 --- a/test/PluginTest.php +++ b/test/PluginTest.php @@ -19,6 +19,7 @@ use Composer\Package\Version\VersionSelector; use Composer\Repository\RepositoryManager; use Composer\Repository\WritableRepositoryInterface; +use Composer\Script\Event; use org\bovigo\vfs\vfsStream; use PHPUnit\Framework\Assert; use PHPUnit\Framework\TestCase; @@ -144,9 +145,21 @@ public function testSubscribesToExpectedEvents() $subscribers = Plugin::getSubscribedEvents(); $this->assertArrayHasKey('post-package-install', $subscribers); $this->assertArrayHasKey('post-package-update', $subscribers); + $this->assertArrayHasKey('post-install-cmd', $subscribers); + $this->assertArrayHasKey('post-update-cmd', $subscribers); $this->assertEquals('onPostPackage', $subscribers['post-package-install']); $this->assertEquals('onPostPackage', $subscribers['post-package-update']); + $this->assertEquals('onPostCommand', $subscribers['post-install-cmd']); + $this->assertEquals('onPostCommand', $subscribers['post-update-cmd']); + } + + private function getCommandEvent($isDevMode = true) + { + $event = $this->prophesize(Event::class); + $event->isDevMode()->willReturn($isDevMode); + + return $event->reveal(); } public function testDoNothingIfItIsNotInDevMode() @@ -155,6 +168,7 @@ public function testDoNothingIfItIsNotInDevMode() $event->isDevMode()->willReturn(false); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent(false))); } public function testDoNothingInNoInteractionMode() @@ -184,6 +198,7 @@ public function testDoNothingInNoInteractionMode() $this->io->isInteractive()->willReturn(false); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); } public function testDoNothingWhenThereIsNoExtraDependencies() @@ -201,6 +216,7 @@ public function testDoNothingWhenThereIsNoExtraDependencies() $event->getOperation()->willReturn($operation->reveal()); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); } public function testDependencyAlreadyIsInRequiredSection() @@ -230,6 +246,7 @@ public function testDependencyAlreadyIsInRequiredSection() $this->composer->getPackage()->willReturn($rootPackage); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); } public function testDependencyAlreadyIsInRequiredDevSection() @@ -263,6 +280,7 @@ public function testDependencyAlreadyIsInRequiredDevSection() $this->composer->getPackage()->willReturn($rootPackage); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); } public function testInstallSingleDependencyOnPackageUpdate() @@ -337,6 +355,7 @@ public function testInstallSingleDependencyOnPackageUpdate() $this->setUpComposerJson(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); } public function testInstallSingleDependencyOnPackageInstall() @@ -394,6 +413,7 @@ public function testInstallSingleDependencyOnPackageInstall() $this->setUpComposerJson(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); } public function testInstallOneDependenciesWhenOneIsAlreadyInstalled() @@ -459,6 +479,7 @@ public function testInstallOneDependenciesWhenOneIsAlreadyInstalled() $this->setUpComposerJson(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); $json = file_get_contents(vfsStream::url('project/composer.json')); $composer = json_decode($json, true); @@ -536,6 +557,7 @@ public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersion() $this->setUpPool(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); $json = file_get_contents(vfsStream::url('project/composer.json')); $composer = json_decode($json, true); @@ -658,6 +680,7 @@ public function testUpdateComposerWithCurrentlyInstalledVersion() $this->setUpComposerJson(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); $json = file_get_contents(vfsStream::url('project/composer.json')); $composer = json_decode($json, true); @@ -782,6 +805,7 @@ public function testDependencyOrChoosePackageToInstall() $this->setUpPool(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); $json = file_get_contents(vfsStream::url('project/composer.json')); $composer = json_decode($json, true); @@ -860,6 +884,7 @@ public function testDependencyOrOnePackageIsAlreadyInstalledAndShouldBeAddedInto $this->setUpPool(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); $json = file_get_contents(vfsStream::url('project/composer.json')); $composer = json_decode($json, true); @@ -1071,6 +1096,7 @@ public function testIntegrationHandleDependencyAndDependencyOr() $this->setUpPool(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); $json = file_get_contents(vfsStream::url('project/composer.json')); $composer = json_decode($json, true); From 2b5c2f6d6e4a1188e61519e0526123d1639e8424 Mon Sep 17 00:00:00 2001 From: webimpress Date: Fri, 10 Nov 2017 14:39:10 +0000 Subject: [PATCH 4/7] Tests improvements - test separately onPostCommand and onPostPackage --- test/PluginTest.php | 319 +++++++++++++++----------------------------- 1 file changed, 106 insertions(+), 213 deletions(-) diff --git a/test/PluginTest.php b/test/PluginTest.php index 1f45aef..249cd63 100644 --- a/test/PluginTest.php +++ b/test/PluginTest.php @@ -162,43 +162,100 @@ private function getCommandEvent($isDevMode = true) return $event->reveal(); } - public function testDoNothingIfItIsNotInDevMode() + public function testPostPackageDoNothingInNoDevMode() { $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(false); + $event->getOperation()->shouldNotBeCalled(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + } + + public function testPostCommandDoNothingInNoDevMode() + { $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent(false))); } - public function testDoNothingInNoInteractionMode() + public function testPostPackageDoNothingInNoInteractionMode() { - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ - 'dependency' => [ - 'extra-dependency-foo', - ], + $event = $this->prophesize(PackageEvent::class); + $event->isDevMode()->willReturn(true); + $event->getOperation()->shouldNotBeCalled(); + + $this->io->isInteractive()->willReturn(false); + + $this->assertNull($this->plugin->onPostPackage($event->reveal())); + } + + private function injectPackages(array $packagesToInstall) + { + $p = new ReflectionProperty($this->plugin, 'packagesToInstall'); + $p->setAccessible(true); + + $p->setValue($this->plugin, $packagesToInstall); + } + + public function testPostCommandDoNothingInNoInteractionMode() + { + $this->injectPackages([ + 'my-package-foo' => '2.37.1', + 'other-package' => 'dev-feature/branch', ]); - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); + $this->io->isInteractive()->willReturn(false); - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); + $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); + } + + public function testPostCommandInstallPackagesAndUpdateComposer() + { + $this->injectPackages([ + 'my-package-foo' => '2.37.1', + 'other-package' => 'dev-feature/branch', + ]); + + $this->io->isInteractive()->willReturn(true); + $this->io->write(' Updating composer.json')->shouldBeCalled(); + $this->io->write('Updating root package')->shouldBeCalled(); + $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); + + $config = $this->prophesize(Config::class); + $config->get('sort-packages')->willReturn(true); + $config->get(Argument::any())->willReturn(null); $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn([]); $rootPackage->getDevRequires()->willReturn([]); + $rootPackage->setRequires(Argument::that(function (array $arguments) { + if (count($arguments) !== 2) { + return false; + } + + if (! $this->assertSetRequiresArgument('my-package-foo', '2.37.1', $arguments)) { + return false; + } + + if (! $this->assertSetRequiresArgument('other-package', 'dev-feature/branch', $arguments)) { + return false; + } + + return true; + }))->shouldBeCalled(); $this->composer->getPackage()->willReturn($rootPackage); + $this->composer->getConfig()->willReturn($config->reveal()); - $this->io->isInteractive()->willReturn(false); + $this->setUpComposerJson(); + $this->setUpComposerInstaller(['my-package-foo', 'other-package']); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); + + $json = file_get_contents(vfsStream::url('project/composer.json')); + $composer = json_decode($json, true); + $this->assertTrue(isset($composer['require']['my-package-foo'])); + $this->assertSame('2.37.1', $composer['require']['my-package-foo']); + $this->assertTrue(isset($composer['require']['other-package'])); + $this->assertSame('dev-feature/branch', $composer['require']['other-package']); } public function testDoNothingWhenThereIsNoExtraDependencies() @@ -213,13 +270,15 @@ public function testDoNothingWhenThereIsNoExtraDependencies() $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); + $event->getOperation()->willReturn($operation->reveal())->shouldBeCalled(); + + $this->io->isInteractive()->willReturn(true)->shouldBeCalled(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); - $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); + $this->assertPackagesToInstall([]); } - public function testDependencyAlreadyIsInRequiredSection() + public function testDependencyAlreadyIsInRequireSection() { /** @var PackageInterface|ObjectProphecy $package */ $package = $this->prophesize(PackageInterface::class); @@ -235,21 +294,25 @@ public function testDependencyAlreadyIsInRequiredSection() $event = $this->prophesize(PackageEvent::class); $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); + $event->getOperation()->willReturn($operation->reveal())->shouldBeCalled(); + + $this->io->isInteractive()->willReturn(true)->shouldBeCalled(); + $this->io->askAndValidate(Argument::any())->shouldNotBeCalled(); $link = $this->prophesize(Link::class); $link->getTarget()->willReturn('extra-dependency-foo'); $rootPackage = $this->prophesize(RootPackageInterface::class); - $rootPackage->getDevRequires()->willReturn(['extra-dependency-foo' => $link->reveal()]); + $rootPackage->getRequires()->willReturn(['extra-dependency-foo' => $link->reveal()])->shouldBeCalled(); + $rootPackage->getDevRequires()->willReturn([])->shouldBeCalled(); $this->composer->getPackage()->willReturn($rootPackage); $this->assertNull($this->plugin->onPostPackage($event->reveal())); - $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); + $this->assertPackagesToInstall([]); } - public function testDependencyAlreadyIsInRequiredDevSection() + public function testDependencyAlreadyIsInRequireDevSection() { /** @var PackageInterface|ObjectProphecy $package */ $package = $this->prophesize(PackageInterface::class); @@ -280,7 +343,7 @@ public function testDependencyAlreadyIsInRequiredDevSection() $this->composer->getPackage()->willReturn($rootPackage); $this->assertNull($this->plugin->onPostPackage($event->reveal())); - $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); + $this->assertPackagesToInstall([]); } public function testInstallSingleDependencyOnPackageUpdate() @@ -311,17 +374,6 @@ public function testInstallSingleDependencyOnPackageUpdate() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn([]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function (array $arguments) { - if (count($arguments) !== 1) { - return false; - } - - if (! $this->assertSetRequiresArgument('extra-dependency-foo', '17.0.1-dev', $arguments)) { - return false; - } - - return true; - }))->shouldBeCalled(); $this->composer->getPackage()->willReturn($rootPackage); $this->composer->getConfig()->willReturn($config->reveal()); @@ -347,15 +399,8 @@ public function testInstallSingleDependencyOnPackageUpdate() }) )->willReturn('17.0.1-dev'); - $this->io->write(' Updating composer.json')->shouldBeCalled(); - $this->io->write('Updating root package')->shouldBeCalled(); - $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); - - $this->setUpComposerInstaller(['extra-dependency-foo']); - $this->setUpComposerJson(); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); - $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); + $this->assertPackagesToInstall(['extra-dependency-foo' => '17.0.1-dev']); } public function testInstallSingleDependencyOnPackageInstall() @@ -383,17 +428,6 @@ public function testInstallSingleDependencyOnPackageInstall() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn([]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function (array $arguments) { - if (count($arguments) !== 1) { - return false; - } - - if (! $this->assertSetRequiresArgument('extra-dependency-foo', '17.0.1-dev', $arguments)) { - return false; - } - - return true; - }))->shouldBeCalled(); $this->composer->getPackage()->willReturn($rootPackage); $this->composer->getConfig()->willReturn($config->reveal()); @@ -405,15 +439,8 @@ public function testInstallSingleDependencyOnPackageInstall() Argument::type('callable') )->willReturn('17.0.1-dev'); - $this->io->write(' Updating composer.json')->shouldBeCalled(); - $this->io->write('Updating root package')->shouldBeCalled(); - $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); - - $this->setUpComposerInstaller(['extra-dependency-foo']); - $this->setUpComposerJson(); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); - $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); + $this->assertPackagesToInstall(['extra-dependency-foo' => '17.0.1-dev']); } public function testInstallOneDependenciesWhenOneIsAlreadyInstalled() @@ -445,21 +472,6 @@ public function testInstallOneDependenciesWhenOneIsAlreadyInstalled() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn(['extra-dependency-bar' => $link->reveal()]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function (array $arguments) { - if (count($arguments) !== 2) { - return false; - } - - if (! $this->assertSetRequiresArgument('extra-dependency-foo', '17.0.1-dev', $arguments)) { - return false; - } - - if (! isset($arguments['extra-dependency-bar'])) { - return false; - } - - return true; - }))->shouldBeCalled(); $this->composer->getPackage()->willReturn($rootPackage); $this->composer->getConfig()->willReturn($config->reveal()); @@ -471,20 +483,8 @@ public function testInstallOneDependenciesWhenOneIsAlreadyInstalled() Argument::type('callable') )->willReturn('17.0.1-dev'); - $this->io->write(' Updating composer.json')->shouldBeCalled(); - $this->io->write('Updating root package')->shouldBeCalled(); - $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); - - $this->setUpComposerInstaller(['extra-dependency-foo']); - $this->setUpComposerJson(); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); - $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); - - $json = file_get_contents(vfsStream::url('project/composer.json')); - $composer = json_decode($json, true); - $this->assertTrue(isset($composer['require']['extra-dependency-foo'])); - $this->assertSame('17.0.1-dev', $composer['require']['extra-dependency-foo']); + $this->assertPackagesToInstall(['extra-dependency-foo' => '17.0.1-dev']); } public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersion() @@ -512,17 +512,6 @@ public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersion() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn([]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function (array $arguments) { - if (count($arguments) !== 1) { - return false; - } - - if (! $this->assertSetRequiresArgument('extra-dependency-foo', '13.4.2', $arguments)) { - return false; - } - - return true; - }))->shouldBeCalled(); $rootPackage->getMinimumStability()->willReturn('stable'); $this->composer->getPackage()->willReturn($rootPackage); @@ -535,14 +524,8 @@ public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersion() Argument::type('callable') )->willReturn(false); - $this->io->write(' Updating composer.json')->shouldBeCalled(); - $this->io->write('Updating root package')->shouldBeCalled(); - $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); $this->io->write('Using version 13.4.2 for extra-dependency-foo')->shouldBeCalled(); - $this->setUpComposerInstaller(['extra-dependency-foo']); - $this->setUpComposerJson(); - $package = $this->prophesize(PackageInterface::class); $versionSelector = $this->prophesize(VersionSelector::class); @@ -557,12 +540,7 @@ public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersion() $this->setUpPool(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); - $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); - - $json = file_get_contents(vfsStream::url('project/composer.json')); - $composer = json_decode($json, true); - $this->assertTrue(isset($composer['require']['extra-dependency-foo'])); - $this->assertSame('13.4.2', $composer['require']['extra-dependency-foo']); + $this->assertPackagesToInstall(['extra-dependency-foo' => '13.4.2']); } public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersionNotFoundMatchingPackage() @@ -649,17 +627,6 @@ public function testUpdateComposerWithCurrentlyInstalledVersion() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn([]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function (array $arguments) { - if (count($arguments) !== 1) { - return false; - } - - if (! $this->assertSetRequiresArgument('extra-dependency-foo', '^0.5.1', $arguments)) { - return false; - } - - return true; - }))->shouldBeCalled(); $rootPackage->getMinimumStability()->willReturn('stable'); $this->composer->getPackage()->willReturn($rootPackage); @@ -672,20 +639,9 @@ public function testUpdateComposerWithCurrentlyInstalledVersion() . ' ^0.5.1; to upgrade, run composer require extra-dependency-foo:VERSION' ) ->shouldBeCalled(); - $this->io->write(' Updating composer.json')->shouldBeCalled(); - $this->io->write('Updating root package')->shouldBeCalled(); - $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); - - $this->setUpComposerInstaller(['extra-dependency-foo']); - $this->setUpComposerJson(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); - $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); - - $json = file_get_contents(vfsStream::url('project/composer.json')); - $composer = json_decode($json, true); - $this->assertTrue(isset($composer['require']['extra-dependency-foo'])); - $this->assertSame('^0.5.1', $composer['require']['extra-dependency-foo']); + $this->assertPackagesToInstall(['extra-dependency-foo' => '^0.5.1']); } public function testDependencyOrChoosePackageToInstall() @@ -716,17 +672,6 @@ public function testDependencyOrChoosePackageToInstall() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn([]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function (array $arguments) { - if (count($arguments) !== 1) { - return false; - } - - if (! $this->assertSetRequiresArgument('extra-dependency-bar', '13.4.2', $arguments)) { - return false; - } - - return true; - }))->shouldBeCalled(); $rootPackage->getMinimumStability()->willReturn('stable'); $this->composer->getPackage()->willReturn($rootPackage); @@ -783,14 +728,8 @@ public function testDependencyOrChoosePackageToInstall() ->willReturn(false) ->shouldBeCalledTimes(1); - $this->io->write(' Updating composer.json')->shouldBeCalled(); - $this->io->write('Updating root package')->shouldBeCalled(); - $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); $this->io->write('Using version 13.4.2 for extra-dependency-bar')->shouldBeCalled(); - $this->setUpComposerInstaller(['extra-dependency-bar']); - $this->setUpComposerJson(); - $package = $this->prophesize(PackageInterface::class); $versionSelector = $this->prophesize(VersionSelector::class); @@ -805,12 +744,7 @@ public function testDependencyOrChoosePackageToInstall() $this->setUpPool(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); - $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); - - $json = file_get_contents(vfsStream::url('project/composer.json')); - $composer = json_decode($json, true); - $this->assertTrue(isset($composer['require']['extra-dependency-bar'])); - $this->assertSame('13.4.2', $composer['require']['extra-dependency-bar']); + $this->assertPackagesToInstall(['extra-dependency-bar' => '13.4.2']); } public function testDependencyOrOnePackageIsAlreadyInstalledAndShouldBeAddedIntoRootComposer() @@ -848,17 +782,6 @@ public function testDependencyOrOnePackageIsAlreadyInstalledAndShouldBeAddedInto $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn([]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function (array $arguments) { - if (count($arguments) !== 1) { - return false; - } - - if (! $this->assertSetRequiresArgument('extra-dependency-baz', '^3.7.1', $arguments)) { - return false; - } - - return true; - }))->shouldBeCalled(); $rootPackage->getMinimumStability()->willReturn('stable'); $this->composer->getPackage()->willReturn($rootPackage); @@ -875,21 +798,10 @@ public function testDependencyOrOnePackageIsAlreadyInstalledAndShouldBeAddedInto ->askAndValidate(Argument::any(), Argument::any()) ->shouldNotBeCalled(); - $this->io->write(' Updating composer.json')->shouldBeCalled(); - $this->io->write('Updating root package')->shouldBeCalled(); - $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); - - $this->setUpComposerInstaller(['extra-dependency-baz']); - $this->setUpComposerJson(); $this->setUpPool(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); - $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); - - $json = file_get_contents(vfsStream::url('project/composer.json')); - $composer = json_decode($json, true); - $this->assertTrue(isset($composer['require']['extra-dependency-baz'])); - $this->assertSame('^3.7.1', $composer['require']['extra-dependency-baz']); + $this->assertPackagesToInstall(['extra-dependency-baz' => '^3.7.1']); } public function testDependencyOrOnePackageIsAlreadyInRootComposer() @@ -923,7 +835,6 @@ public function testDependencyOrOnePackageIsAlreadyInRootComposer() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn(['extra-dependency-foo' => $link]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::any())->shouldNotBeCalled(); $rootPackage->getMinimumStability()->willReturn('stable'); $this->composer->getPackage()->willReturn($rootPackage); @@ -935,6 +846,7 @@ public function testDependencyOrOnePackageIsAlreadyInRootComposer() ->shouldNotBeCalled(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertPackagesToInstall([]); } public function testDependencyOrWrongDefinitionThrowsException() @@ -966,7 +878,6 @@ public function testDependencyOrWrongDefinitionThrowsException() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn(['extra-dependency-foo' => $link]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::any())->shouldNotBeCalled(); $rootPackage->getMinimumStability()->willReturn('stable'); $this->composer->getPackage()->willReturn($rootPackage); @@ -1014,21 +925,6 @@ public function testIntegrationHandleDependencyAndDependencyOr() $rootPackage = $this->prophesize(RootPackageInterface::class); $rootPackage->getRequires()->willReturn([]); $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function (array $arguments) { - if (count($arguments) !== 2) { - return false; - } - - if (! $this->assertSetRequiresArgument('extra-package-required', '3.9.1', $arguments)) { - return false; - } - - if (! $this->assertSetRequiresArgument('extra-choose-two', '2.1.5', $arguments)) { - return false; - } - - return true; - }))->shouldBeCalled(); $rootPackage->getMinimumStability()->willReturn('stable'); $this->composer->getPackage()->willReturn($rootPackage); @@ -1072,15 +968,9 @@ public function testIntegrationHandleDependencyAndDependencyOr() ->willReturn(false) ->shouldBeCalledTimes(1); - $this->io->write(' Updating composer.json')->shouldBeCalled(); - $this->io->write('Updating root package')->shouldBeCalled(); - $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); $this->io->write('Using version 3.9.1 for extra-package-required')->shouldBeCalled(); $this->io->write('Using version 2.1.5 for extra-choose-two')->shouldBeCalled(); - $this->setUpComposerInstaller(['extra-package-required', 'extra-choose-two']); - $this->setUpComposerJson(); - $versionSelector = $this->prophesize(VersionSelector::class); $versionSelector->findBestCandidate('extra-package-required', null, null, 'stable') ->willReturn($package->reveal()) @@ -1096,14 +986,10 @@ public function testIntegrationHandleDependencyAndDependencyOr() $this->setUpPool(); $this->assertNull($this->plugin->onPostPackage($event->reveal())); - $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); - - $json = file_get_contents(vfsStream::url('project/composer.json')); - $composer = json_decode($json, true); - $this->assertTrue(isset($composer['require']['extra-package-required'])); - $this->assertSame('3.9.1', $composer['require']['extra-package-required']); - $this->assertTrue(isset($composer['require']['extra-choose-two'])); - $this->assertSame('2.1.5', $composer['require']['extra-choose-two']); + $this->assertPackagesToInstall([ + 'extra-package-required' => '3.9.1', + 'extra-choose-two' => '2.1.5', + ]); } private function assertSetRequiresArgument($name, $version, array $arguments) @@ -1133,6 +1019,13 @@ private function assertSetRequiresArgument($name, $version, array $arguments) return true; } + private function assertPackagesToInstall(array $packagesToInstall) + { + $p = new ReflectionProperty($this->plugin, 'packagesToInstall'); + $p->setAccessible(true); + self::assertSame($packagesToInstall, $p->getValue($this->plugin)); + } + public function testComposerInstallerFactory() { $r = new ReflectionProperty($this->plugin, 'installerFactory'); From b90d35a093130df6b67837c6b61e3dbb62d66a16 Mon Sep 17 00:00:00 2001 From: webimpress Date: Fri, 10 Nov 2017 15:12:41 +0000 Subject: [PATCH 5/7] Tests improvements --- test/PluginTest.php | 623 ++++++++++++++++++-------------------------- 1 file changed, 256 insertions(+), 367 deletions(-) diff --git a/test/PluginTest.php b/test/PluginTest.php index 249cd63..4b85672 100644 --- a/test/PluginTest.php +++ b/test/PluginTest.php @@ -65,7 +65,24 @@ protected function setUp() $this->plugin->activate($this->composer->reveal(), $this->io->reveal()); } - protected function setUpComposerInstaller(array $expectedPackages, $expectedReturn = 0) + private function setUpRootPackage( + array $dependencies = [], + array $devDependencies = [], + $minimumStability = null + ) { + $rootPackage = $this->prophesize(RootPackageInterface::class); + $rootPackage->getRequires()->willReturn($dependencies)->shouldBeCalled(); + $rootPackage->getDevRequires()->willReturn($devDependencies)->shouldBeCalled(); + if ($minimumStability) { + $rootPackage->getMinimumStability()->willReturn($minimumStability)->shouldBeCalled(); + } else { + $rootPackage->getMinimumStability()->shouldNotBeCalled(); + } + + $this->composer->getPackage()->willReturn($rootPackage->reveal())->shouldBeCalled(); + } + + private function setUpComposerInstaller(array $expectedPackages, $expectedReturn = 0) { $installer = $this->prophesize(Installer::class); $installer->setRunScripts(false)->shouldBeCalled(); @@ -81,7 +98,7 @@ protected function setUpComposerInstaller(array $expectedPackages, $expectedRetu }); } - protected function setUpVersionSelector(VersionSelector $versionSelector) + private function setUpVersionSelector(VersionSelector $versionSelector) { $r = new ReflectionProperty($this->plugin, 'versionSelectorFactory'); $r->setAccessible(true); @@ -90,7 +107,7 @@ protected function setUpVersionSelector(VersionSelector $versionSelector) }); } - protected function setUpPool() + private function setUpPool() { $pool = $this->prophesize(Pool::class); @@ -99,7 +116,7 @@ protected function setUpPool() $r->setValue($this->plugin, $pool->reveal()); } - protected function setUpComposerJson($data = null) + private function setUpComposerJson($data = null) { $project = vfsStream::setup('project'); vfsStream::newFile('composer.json') @@ -113,13 +130,13 @@ protected function setUpComposerJson($data = null) }); } - protected function createComposerJson($data) + private function createComposerJson($data) { $data = $data ?: $this->getDefaultComposerData(); return json_encode($data); } - protected function getDefaultComposerData() + private function getDefaultComposerData() { return [ 'name' => 'test/project', @@ -131,6 +148,46 @@ protected function getDefaultComposerData() ]; } + private function getCommandEvent($isDevMode = true) + { + $event = $this->prophesize(Event::class); + $event->isDevMode()->willReturn($isDevMode); + + return $event->reveal(); + } + + private function getPackageEvent( + $packageName, + array $extra, + $operationClass = InstallOperation::class + ) { + /** @var PackageInterface|ObjectProphecy $package */ + $package = $this->prophesize(PackageInterface::class); + $package->getName()->willReturn($packageName); + $package->getExtra()->willReturn($extra); + + $operation = $this->prophesize($operationClass); + if ($operationClass === InstallOperation::class) { + $operation->getPackage()->willReturn($package->reveal()); + } else { + $operation->getTargetPackage()->willReturn($package->reveal()); + } + + $event = $this->prophesize(PackageEvent::class); + $event->isDevMode()->willReturn(true); + $event->getOperation()->willReturn($operation->reveal())->shouldBeCalled(); + + return $event->reveal(); + } + + private function injectPackages(array $packagesToInstall) + { + $p = new ReflectionProperty($this->plugin, 'packagesToInstall'); + $p->setAccessible(true); + + $p->setValue($this->plugin, $packagesToInstall); + } + public function testActivateSetsComposerAndIoProperties() { $plugin = new Plugin(); @@ -154,14 +211,6 @@ public function testSubscribesToExpectedEvents() $this->assertEquals('onPostCommand', $subscribers['post-update-cmd']); } - private function getCommandEvent($isDevMode = true) - { - $event = $this->prophesize(Event::class); - $event->isDevMode()->willReturn($isDevMode); - - return $event->reveal(); - } - public function testPostPackageDoNothingInNoDevMode() { $event = $this->prophesize(PackageEvent::class); @@ -187,14 +236,6 @@ public function testPostPackageDoNothingInNoInteractionMode() $this->assertNull($this->plugin->onPostPackage($event->reveal())); } - private function injectPackages(array $packagesToInstall) - { - $p = new ReflectionProperty($this->plugin, 'packagesToInstall'); - $p->setAccessible(true); - - $p->setValue($this->plugin, $packagesToInstall); - } - public function testPostCommandDoNothingInNoInteractionMode() { $this->injectPackages([ @@ -207,12 +248,38 @@ public function testPostCommandDoNothingInNoInteractionMode() $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); } - public function testPostCommandInstallPackagesAndUpdateComposer() + public function sortPackages() { - $this->injectPackages([ - 'my-package-foo' => '2.37.1', - 'other-package' => 'dev-feature/branch', - ]); + return [ + [ + true, + [ + 'zoo/bar' => '1.2.4', + 'foo/baz' => '2.7.3', + ], + '{"foo\/baz":"2.7.3","webimpress\/my-package":"^1.0.0-dev@dev","zoo\/bar":"1.2.4"}', + ], + [ + false, + [ + 'zoo/bar' => '1.2.4', + 'foo/baz' => '2.7.3', + ], + '{"webimpress\/my-package":"^1.0.0-dev@dev","zoo\/bar":"1.2.4","foo\/baz":"2.7.3"}', + ], + ]; + } + + /** + * @dataProvider sortPackages + * + * @param bool $sortPackages + * @param array $packages + * @param string $result + */ + public function testPostCommandInstallPackagesAndUpdateComposer($sortPackages, array $packages, $result) + { + $this->injectPackages($packages); $this->io->isInteractive()->willReturn(true); $this->io->write(' Updating composer.json')->shouldBeCalled(); @@ -220,81 +287,77 @@ public function testPostCommandInstallPackagesAndUpdateComposer() $this->io->write(' Running an update to install dependent packages')->shouldBeCalled(); $config = $this->prophesize(Config::class); - $config->get('sort-packages')->willReturn(true); - $config->get(Argument::any())->willReturn(null); + $config->get('sort-packages')->willReturn($sortPackages)->shouldBeCalled(); $rootPackage = $this->prophesize(RootPackageInterface::class); - $rootPackage->getRequires()->willReturn([]); - $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->setRequires(Argument::that(function (array $arguments) { - if (count($arguments) !== 2) { - return false; - } - - if (! $this->assertSetRequiresArgument('my-package-foo', '2.37.1', $arguments)) { + $rootPackage->getRequires()->willReturn([])->shouldBeCalled(); + $rootPackage->getDevRequires()->willReturn([])->shouldNotBeCalled(); + $rootPackage->setRequires(Argument::that(function (array $arguments) use ($packages) { + if (count($arguments) !== count($packages)) { return false; } - if (! $this->assertSetRequiresArgument('other-package', 'dev-feature/branch', $arguments)) { - return false; + foreach ($packages as $package => $version) { + if (! $this->assertSetRequiresArgument($package, $version, $arguments)) { + return false; + } } return true; }))->shouldBeCalled(); - $this->composer->getPackage()->willReturn($rootPackage); - $this->composer->getConfig()->willReturn($config->reveal()); + $this->composer->getPackage()->willReturn($rootPackage)->shouldBeCalled(); + $this->composer->getConfig()->willReturn($config->reveal())->shouldBeCalled(); $this->setUpComposerJson(); - $this->setUpComposerInstaller(['my-package-foo', 'other-package']); + $this->setUpComposerInstaller(array_keys($packages)); $this->assertNull($this->plugin->onPostCommand($this->getCommandEvent())); $json = file_get_contents(vfsStream::url('project/composer.json')); $composer = json_decode($json, true); - $this->assertTrue(isset($composer['require']['my-package-foo'])); - $this->assertSame('2.37.1', $composer['require']['my-package-foo']); - $this->assertTrue(isset($composer['require']['other-package'])); - $this->assertSame('dev-feature/branch', $composer['require']['other-package']); + foreach ($packages as $package => $version) { + $this->assertTrue(isset($composer['require'][$package])); + $this->assertSame($version, $composer['require'][$package]); + } + $this->assertSame($result, json_encode($composer['require'])); } - public function testDoNothingWhenThereIsNoExtraDependencies() + public function operation() { - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([]); - - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); + return [ + 'install' => [InstallOperation::class], + 'update' => [UpdateOperation::class], + ]; + } - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal())->shouldBeCalled(); + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testDoNothingWhenThereIsNoExtraDependencies($operation) + { + $event = $this->getPackageEvent('some/component', [], $operation); $this->io->isInteractive()->willReturn(true)->shouldBeCalled(); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostPackage($event)); $this->assertPackagesToInstall([]); } - public function testDependencyAlreadyIsInRequireSection() + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testDependencyAlreadyIsInRequireSection($operation) { - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ + $event = $this->getPackageEvent('some/component', [ 'dependency' => [ 'extra-dependency-foo', ], - ]); - - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); - - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal())->shouldBeCalled(); + ], $operation); $this->io->isInteractive()->willReturn(true)->shouldBeCalled(); $this->io->askAndValidate(Argument::any())->shouldNotBeCalled(); @@ -308,27 +371,22 @@ public function testDependencyAlreadyIsInRequireSection() $this->composer->getPackage()->willReturn($rootPackage); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostPackage($event)); $this->assertPackagesToInstall([]); } - public function testDependencyAlreadyIsInRequireDevSection() + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testDependencyAlreadyIsInRequireDevSection($operation) { - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ + $event = $this->getPackageEvent('some/component', [ 'dependency' => [ 'extra-dependency-foo', ], - ]); - - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); - - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); + ], $operation); $this->io->isInteractive()->willReturn(true)->shouldBeCalled(); $this->io->askAndValidate(Argument::any())->shouldNotBeCalled(); @@ -342,27 +400,22 @@ public function testDependencyAlreadyIsInRequireDevSection() $this->composer->getPackage()->willReturn($rootPackage); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostPackage($event)); $this->assertPackagesToInstall([]); } - public function testInstallSingleDependencyOnPackageUpdate() + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testInstallSingleDependency($operation) { - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ + $event = $this->getPackageEvent('some/component', [ 'dependency' => [ 'extra-dependency-foo', ], - ]); - - $operation = $this->prophesize(UpdateOperation::class); - $operation->getTargetPackage()->willReturn($package->reveal()); - - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); + ], $operation); $this->io->isInteractive()->willReturn(true)->shouldBeCalled(); $this->io->askAndValidate(Argument::any())->shouldNotBeCalled(); @@ -399,68 +452,23 @@ public function testInstallSingleDependencyOnPackageUpdate() }) )->willReturn('17.0.1-dev'); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostPackage($event)); $this->assertPackagesToInstall(['extra-dependency-foo' => '17.0.1-dev']); } - public function testInstallSingleDependencyOnPackageInstall() + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testInstallOneDependenciesWhenOneIsAlreadyInstalled($operation) { - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ - 'dependency' => [ - 'extra-dependency-foo', - ], - ]); - - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); - - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); - - $config = $this->prophesize(Config::class); - $config->get('sort-packages')->willReturn(true); - $config->get(Argument::any())->willReturn(null); - - $rootPackage = $this->prophesize(RootPackageInterface::class); - $rootPackage->getRequires()->willReturn([]); - $rootPackage->getDevRequires()->willReturn([]); - - $this->composer->getPackage()->willReturn($rootPackage); - $this->composer->getConfig()->willReturn($config->reveal()); - - $this->io->isInteractive()->willReturn(true); - $this->io->askAndValidate( - 'Enter the version of extra-dependency-foo to require' - . ' (or leave blank to use the latest version): ', - Argument::type('callable') - )->willReturn('17.0.1-dev'); - - $this->assertNull($this->plugin->onPostPackage($event->reveal())); - $this->assertPackagesToInstall(['extra-dependency-foo' => '17.0.1-dev']); - } - - public function testInstallOneDependenciesWhenOneIsAlreadyInstalled() - { - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ + $event = $this->getPackageEvent('some/component', [ 'dependency' => [ 'extra-dependency-foo', 'extra-dependency-bar', ], - ]); - - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); - - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); + ], $operation); $config = $this->prophesize(Config::class); $config->get('sort-packages')->willReturn(true); @@ -483,39 +491,24 @@ public function testInstallOneDependenciesWhenOneIsAlreadyInstalled() Argument::type('callable') )->willReturn('17.0.1-dev'); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostPackage($event)); $this->assertPackagesToInstall(['extra-dependency-foo' => '17.0.1-dev']); } - public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersion() + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersion($operation) { - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ + $event = $this->getPackageEvent('some/component', [ 'dependency' => [ 'extra-dependency-foo', ], - ]); - - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); - - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); - - $config = $this->prophesize(Config::class); - $config->get('sort-packages')->willReturn(true); - $config->get(Argument::any())->willReturn(null); - - $rootPackage = $this->prophesize(RootPackageInterface::class); - $rootPackage->getRequires()->willReturn([]); - $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->getMinimumStability()->willReturn('stable'); + ], $operation); - $this->composer->getPackage()->willReturn($rootPackage); - $this->composer->getConfig()->willReturn($config->reveal()); + $this->setUpRootPackage(); $this->io->isInteractive()->willReturn(true); $this->io->askAndValidate( @@ -539,39 +532,24 @@ public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersion() $this->setUpVersionSelector($versionSelector->reveal()); $this->setUpPool(); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostPackage($event)); $this->assertPackagesToInstall(['extra-dependency-foo' => '13.4.2']); } - public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersionNotFoundMatchingPackage() + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersionNotFoundMatchingPackage($operation) { - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ + $event = $this->getPackageEvent('some/component', [ 'dependency' => [ 'extra-dependency-foo', ], - ]); + ], $operation); - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); - - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); - - $config = $this->prophesize(Config::class); - $config->get('sort-packages')->willReturn(true); - $config->get(Argument::any())->willReturn(null); - - $rootPackage = $this->prophesize(RootPackageInterface::class); - $rootPackage->getRequires()->willReturn([]); - $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->getMinimumStability()->willReturn('stable'); - - $this->composer->getPackage()->willReturn($rootPackage); - $this->composer->getConfig()->willReturn($config->reveal()); + $this->setUpRootPackage([], [], 'stable-foo'); $this->io->isInteractive()->willReturn(true); $this->io->askAndValidate( @@ -580,22 +558,27 @@ public function testInstallSingleDependencyAndAutomaticallyChooseLatestVersionNo Argument::type('callable') )->willReturn(false); - $this->setUpComposerJson(); - $versionSelector = $this->prophesize(VersionSelector::class); - $versionSelector->findBestCandidate('extra-dependency-foo', null, null, 'stable')->willReturn(null); + $versionSelector->findBestCandidate('extra-dependency-foo', null, null, 'stable') + ->willReturn(null) + ->shouldBeCalledTimes(1); $this->setUpVersionSelector($versionSelector->reveal()); $this->setUpPool(); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage( - 'Could not find package extra-dependency-foo at any version for your minimum-stability' + 'Could not find package extra-dependency-foo at any version for your minimum-stability (stable-foo)' ); - $this->plugin->onPostPackage($event->reveal()); + $this->plugin->onPostPackage($event); } - public function testUpdateComposerWithCurrentlyInstalledVersion() + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testUpdateComposerWithCurrentlyInstalledVersion($operation) { $installedPackage = $this->prophesize(PackageInterface::class); $installedPackage->getName()->willReturn('extra-dependency-foo'); @@ -604,33 +587,13 @@ public function testUpdateComposerWithCurrentlyInstalledVersion() $this->localRepository->getPackages()->willReturn([$installedPackage->reveal()]); $this->plugin->activate($this->composer->reveal(), $this->io->reveal()); - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ + $event = $this->getPackageEvent('some/component', [ 'dependency' => [ 'extra-dependency-foo', ], - ]); - - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); - - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); + ], $operation); - $config = $this->prophesize(Config::class); - $config->get('sort-packages')->willReturn(true); - $config->get(Argument::any())->willReturn(null); - - $rootPackage = $this->prophesize(RootPackageInterface::class); - $rootPackage->getRequires()->willReturn([]); - $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->getMinimumStability()->willReturn('stable'); - - $this->composer->getPackage()->willReturn($rootPackage); - $this->composer->getConfig()->willReturn($config->reveal()); + $this->setUpRootPackage(); $this->io->isInteractive()->willReturn(true); $this->io @@ -640,42 +603,27 @@ public function testUpdateComposerWithCurrentlyInstalledVersion() ) ->shouldBeCalled(); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostPackage($event)); $this->assertPackagesToInstall(['extra-dependency-foo' => '^0.5.1']); } - public function testDependencyOrChoosePackageToInstall() + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testDependencyOrChoosePackageToInstall($operation) { - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ + $event = $this->getPackageEvent('some/component', [ 'dependency-or' => [ 'My question foo bar baz' => [ 'extra-dependency-foo', 'extra-dependency-bar', ], ], - ]); + ], $operation); - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); - - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); - - $config = $this->prophesize(Config::class); - $config->get('sort-packages')->willReturn(true); - $config->get(Argument::any())->willReturn(null); - - $rootPackage = $this->prophesize(RootPackageInterface::class); - $rootPackage->getRequires()->willReturn([]); - $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->getMinimumStability()->willReturn('stable'); - - $this->composer->getPackage()->willReturn($rootPackage); - $this->composer->getConfig()->willReturn($config->reveal()); + $this->setUpRootPackage(); $this->io->isInteractive()->willReturn(true); $this->io @@ -743,11 +691,16 @@ public function testDependencyOrChoosePackageToInstall() $this->setUpVersionSelector($versionSelector->reveal()); $this->setUpPool(); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostPackage($event)); $this->assertPackagesToInstall(['extra-dependency-bar' => '13.4.2']); } - public function testDependencyOrOnePackageIsAlreadyInstalledAndShouldBeAddedIntoRootComposer() + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testDependencyOrOnePackageIsAlreadyInstalledAndShouldBeAddedIntoRootComposer($operation) { $installedPackage = $this->prophesize(PackageInterface::class); $installedPackage->getName()->willReturn('extra-dependency-baz'); @@ -756,36 +709,16 @@ public function testDependencyOrOnePackageIsAlreadyInstalledAndShouldBeAddedInto $this->localRepository->getPackages()->willReturn([$installedPackage->reveal()]); $this->plugin->activate($this->composer->reveal(), $this->io->reveal()); - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ + $event = $this->getPackageEvent('some/component', [ 'dependency-or' => [ 'Choose something' => [ 'extra-dependency-bar', 'extra-dependency-baz', ], ], - ]); - - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); + ], $operation); - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); - - $config = $this->prophesize(Config::class); - $config->get('sort-packages')->willReturn(true); - $config->get(Argument::any())->willReturn(null); - - $rootPackage = $this->prophesize(RootPackageInterface::class); - $rootPackage->getRequires()->willReturn([]); - $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->getMinimumStability()->willReturn('stable'); - - $this->composer->getPackage()->willReturn($rootPackage); - $this->composer->getConfig()->willReturn($config->reveal()); + $this->setUpRootPackage(); $this->io->isInteractive()->willReturn(true); $this->io @@ -800,88 +733,53 @@ public function testDependencyOrOnePackageIsAlreadyInstalledAndShouldBeAddedInto $this->setUpPool(); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostPackage($event)); $this->assertPackagesToInstall(['extra-dependency-baz' => '^3.7.1']); } - public function testDependencyOrOnePackageIsAlreadyInRootComposer() + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testDependencyOrOnePackageIsAlreadyInRootComposer($operation) { - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ + $event = $this->getPackageEvent('some/component', [ 'dependency-or' => [ 'Choose something' => [ 'extra-dependency-foo', 'extra-dependency-baz', ], ], - ]); - - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); - - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); - - $config = $this->prophesize(Config::class); - $config->get('sort-packages')->willReturn(true); - $config->get(Argument::any())->willReturn(null); + ], $operation); $link = $this->prophesize(Link::class); $link->getTarget()->willReturn('extra-dependency-bar'); - $rootPackage = $this->prophesize(RootPackageInterface::class); - $rootPackage->getRequires()->willReturn(['extra-dependency-foo' => $link]); - $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->getMinimumStability()->willReturn('stable'); - - $this->composer->getPackage()->willReturn($rootPackage); - $this->composer->getConfig()->willReturn($config->reveal()); + $this->setUpRootPackage(['extra-dependency-foo' => $link]); $this->io->isInteractive()->willReturn(true)->shouldBeCalledTimes(1); $this->io ->askAndValidate(Argument::any(), Argument::any()) ->shouldNotBeCalled(); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostPackage($event)); $this->assertPackagesToInstall([]); } - public function testDependencyOrWrongDefinitionThrowsException() + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testDependencyOrWrongDefinitionThrowsException($operation) { - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ + $event = $this->getPackageEvent('some/component', [ 'dependency-or' => [ 'extra-dependency-foo', 'extra-dependency-baz', ], - ]); - - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); - - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); - - $config = $this->prophesize(Config::class); - $config->get('sort-packages')->willReturn(true); - $config->get(Argument::any())->willReturn(null); - - $link = $this->prophesize(Link::class); - $link->getTarget()->willReturn('extra-dependency-bar'); - - $rootPackage = $this->prophesize(RootPackageInterface::class); - $rootPackage->getRequires()->willReturn(['extra-dependency-foo' => $link]); - $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->getMinimumStability()->willReturn('stable'); - - $this->composer->getPackage()->willReturn($rootPackage); - $this->composer->getConfig()->willReturn($config->reveal()); + ], $operation); $this->io->isInteractive()->willReturn(true)->shouldBeCalledTimes(1); $this->io @@ -890,15 +788,17 @@ public function testDependencyOrWrongDefinitionThrowsException() $this->expectException(RuntimeException::class); $this->expectExceptionMessage('You must provide at least two optional dependencies.'); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostPackage($event)); } - public function testIntegrationHandleDependencyAndDependencyOr() + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testIntegrationHandleDependencyAndDependencyOr($operation) { - /** @var PackageInterface|ObjectProphecy $package */ - $package = $this->prophesize(PackageInterface::class); - $package->getName()->willReturn('some/component'); - $package->getExtra()->willReturn([ + $event = $this->getPackageEvent('some/component', [ 'dependency' => [ 'extra-package-required', ], @@ -909,26 +809,9 @@ public function testIntegrationHandleDependencyAndDependencyOr() 'extra-choose-three', ], ], - ]); - - $operation = $this->prophesize(InstallOperation::class); - $operation->getPackage()->willReturn($package->reveal()); - - $event = $this->prophesize(PackageEvent::class); - $event->isDevMode()->willReturn(true); - $event->getOperation()->willReturn($operation->reveal()); - - $config = $this->prophesize(Config::class); - $config->get('sort-packages')->willReturn(true); - $config->get(Argument::any())->willReturn(null); + ], $operation); - $rootPackage = $this->prophesize(RootPackageInterface::class); - $rootPackage->getRequires()->willReturn([]); - $rootPackage->getDevRequires()->willReturn([]); - $rootPackage->getMinimumStability()->willReturn('stable'); - - $this->composer->getPackage()->willReturn($rootPackage); - $this->composer->getConfig()->willReturn($config->reveal()); + $this->setUpRootPackage(); $this->io->isInteractive()->willReturn(true); $this->io @@ -971,21 +854,27 @@ public function testIntegrationHandleDependencyAndDependencyOr() $this->io->write('Using version 3.9.1 for extra-package-required')->shouldBeCalled(); $this->io->write('Using version 2.1.5 for extra-choose-two')->shouldBeCalled(); + $package1 = $this->prophesize(PackageInterface::class)->reveal(); + $package2 = $this->prophesize(PackageInterface::class)->reveal(); + $versionSelector = $this->prophesize(VersionSelector::class); $versionSelector->findBestCandidate('extra-package-required', null, null, 'stable') - ->willReturn($package->reveal()) + ->willReturn($package1) ->shouldBeCalledTimes(1); $versionSelector->findBestCandidate('extra-choose-two', null, null, 'stable') - ->willReturn($package->reveal()) + ->willReturn($package2) + ->shouldBeCalledTimes(1); + $versionSelector->findRecommendedRequireVersion($package1) + ->willReturn('3.9.1') + ->shouldBeCalledTimes(1); + $versionSelector->findRecommendedRequireVersion($package2) + ->willReturn('2.1.5') ->shouldBeCalledTimes(1); - $versionSelector->findRecommendedRequireVersion($package->reveal()) - ->willReturn('3.9.1', '2.1.5') - ->shouldBeCalledTimes(2); $this->setUpVersionSelector($versionSelector->reveal()); $this->setUpPool(); - $this->assertNull($this->plugin->onPostPackage($event->reveal())); + $this->assertNull($this->plugin->onPostPackage($event)); $this->assertPackagesToInstall([ 'extra-package-required' => '3.9.1', 'extra-choose-two' => '2.1.5', From e09d76a840f41550c1f0a6c427dee697a821d61a Mon Sep 17 00:00:00 2001 From: webimpress Date: Sun, 12 Nov 2017 21:10:14 +0000 Subject: [PATCH 6/7] Import exception class --- src/Plugin.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Plugin.php b/src/Plugin.php index 9a18921..31cc76f 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -22,6 +22,7 @@ use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryFactory; use Composer\Script\Event; +use InvalidArgumentException; use RuntimeException; class Plugin implements PluginInterface, EventSubscriberInterface @@ -386,7 +387,7 @@ private function findBestVersionForPackage($name) $package = $versionSelector->findBestCandidate($name, null, null, 'stable'); if (! $package) { - throw new \InvalidArgumentException(sprintf( + throw new InvalidArgumentException(sprintf( 'Could not find package %s at any version for your minimum-stability (%s).' . ' Check the package spelling or your minimum-stability', $name, From 0a17a77a364dbafe923b0277f4e95c8243a764d9 Mon Sep 17 00:00:00 2001 From: webimpress Date: Mon, 13 Nov 2017 07:36:32 +0000 Subject: [PATCH 7/7] Do not ask for the same package if it is already prepared to be installed --- src/Plugin.php | 27 +++++++++++++++++- test/PluginTest.php | 69 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/Plugin.php b/src/Plugin.php index 31cc76f..b020657 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -134,6 +134,11 @@ private function andDependencies(array $extra) $packages = array_flip($deps); foreach ($packages as $package => &$constraint) { + if ($this->isPackageReadyToInstall($package)) { + unset($packages[$package]); + continue; + } + if ($this->hasPackage($package)) { unset($packages[$package]); continue; @@ -169,6 +174,11 @@ private function orDependencies(array $extra) } foreach ($options as $package) { + if ($this->isPackageReadyToInstall($package)) { + // Package has been already prepared to be installed, skipping. + continue 2; + } + if ($this->hasPackage($package)) { // Package from this group has been found in root composer, skipping. continue 2; @@ -319,10 +329,25 @@ private function createInstaller(Composer $composer, IOInterface $io, RootPackag private function hasPackage($package) { + $lower = strtolower($package); + $rootPackage = $this->composer->getPackage(); $requires = $rootPackage->getRequires() + $rootPackage->getDevRequires(); foreach ($requires as $name => $link) { - if (strtolower($name) === strtolower($package)) { + if (strtolower($name) === $lower) { + return true; + } + } + + return false; + } + + private function isPackageReadyToInstall($package) + { + $lower = strtolower($package); + + foreach ($this->packagesToInstall as $name => $version) { + if (strtolower($name) === $lower) { return true; } } diff --git a/test/PluginTest.php b/test/PluginTest.php index 4b85672..e10ee58 100644 --- a/test/PluginTest.php +++ b/test/PluginTest.php @@ -607,6 +607,36 @@ public function testUpdateComposerWithCurrentlyInstalledVersion($operation) $this->assertPackagesToInstall(['extra-dependency-foo' => '^0.5.1']); } + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testDoNotInstallAskTwiceForTheSamePackage($operation) + { + $this->injectPackages([ + 'extra-package' => '^1.0.1', + ]); + + $event = $this->getPackageEvent('some/component', [ + 'dependency' => [ + 'extra-package', + ], + ], $operation); + + $this->composer->getPackage()->shouldNotBeCalled(); + + $this->io->isInteractive()->willReturn(true); + $this->io + ->askAndValidate(Argument::any(), Argument::type('callable')) + ->shouldNotBeCalled(); + + $this->assertNull($this->plugin->onPostPackage($event)); + $this->assertPackagesToInstall([ + 'extra-package' => '^1.0.1', + ]); + } + /** * @dataProvider operation * @@ -881,6 +911,45 @@ public function testIntegrationHandleDependencyAndDependencyOr($operation) ]); } + /** + * @dataProvider operation + * + * @param string $operation + */ + public function testIntegrationDoNotAskWhenAlreadyChosen($operation) + { + $event = $this->getPackageEvent('some/component', [ + 'dependency' => [ + 'extra-package-required', + ], + 'dependency-or' => [ + 'Choose something' => [ + 'extra-choose-one', + 'extra-choose-two', + 'extra-choose-three', + 'extra-package-required', + ], + ], + ], $operation); + + $this->setUpRootPackage(); + + $this->io->isInteractive()->willReturn(true); + $this->io + ->askAndValidate( + 'Enter the version of extra-package-required to require' + . ' (or leave blank to use the latest version): ', + Argument::type('callable') + ) + ->willReturn('1.8.3') + ->shouldBeCalledTimes(1); + + $this->assertNull($this->plugin->onPostPackage($event)); + $this->assertPackagesToInstall([ + 'extra-package-required' => '1.8.3', + ]); + } + private function assertSetRequiresArgument($name, $version, array $arguments) { if (! isset($arguments[$name])) {