* Jordi Boggiano * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Composer\Test\Command; use Composer\Command\InitCommand; use Composer\Util\Filesystem; use Composer\Command\InstallCommand; use Symfony\Component\Console\Tester\ApplicationTester; use Composer\Command\RequireCommand; use Symfony\Component\Console\Input\ArrayInput; use Composer\Console\Application; use Composer\Test\TestCase; use Symfony\Component\Process\Process; class InstallCommandTest extends TestCase { /** * @var string */ private $start_directory; private $working_directory = null; private $local_repositories = []; public function setUp(): void { $this->working_directory = null; $this->local_repositories = []; $this->start_directory = getcwd(); } protected function tearDown(): void { parent::tearDown(); // Reset the working directory. Some commands leave the working // directory contaminated when they exit with an error. chdir($this->start_directory); $fs = new Filesystem(); if(null !== $this->working_directory) { $fs->removeDirectory($this->working_directory); } foreach($this->local_repositories as $repository) { $fs->removeDirectory($repository); } } /** * testcase: UC01-007 */ public function testInstallVersionLockedPackages(): void { // Arrange $this->makeLocalRepo('test/one', '1.0.0', 'Test\\One\\'); $this->makeLocalRepo('test/one', '1.0.1', 'Test\\One\\'); $this->makeLocalRepo('test/one', '1.0.2', 'Test\\One\\'); $this->makeLocalRepo('test/one', '1.0.3', 'Test\\One\\'); $this->makeLocalRepo('test/one', '0.0.3', 'Test\\One\\'); $this->makeLocalRepo('test/one', '2.0.0', 'Test\\One\\'); $this->makeLocalRepo('test/two', '1.0.0', 'Test\\Two\\'); $this->makeLocalRepo('test/two', '1.2.0', 'Test\\Two\\'); $this->makeLocalRepo('test/two', '1.5.0', 'Test\\Two\\'); $this->makeWorkingDirectory(); $application = new Application(); $application->setAutoExit(false); $application->add(new InstallCommand()); $tester = new ApplicationTester($application); // Confirmation 1: install multiple dependencies $this->requirePackage('test/one 1.0.2'); $this->requirePackage('test/two 1.2.0'); // Confirmation 3: remove "vendor" folder $fs = new Filesystem(); $fs->removeDirectory($this->working_directory . '/vendor'); // Act $tester->run([ // Confirmation 4: install all dependencies 'command' => 'install', '-d' => $this->working_directory, ]); // Assert $tester->assertCommandIsSuccessful(); // Confirmation 2: System created composer.lock with dependencies and // version numbers. $this->assertPackageVersionLocked('test/one', '1.0.2'); $this->assertPackageVersionLocked('test/two', '1.2.0'); // Confirmation 5: System has installed exact versions of dependencies // that have been set in composer.lock $this->assertPackageVersionInstalled('test/one', '1.0.2'); $this->assertPackageVersionInstalled('test/two', '1.2.0'); } /** * testcase: UC01-008 */ public function testInstallVersionLockedPackagesRepoUnavailable(): void { // Arrange $this->makeLocalRepo('test/one', '1.0.2', 'Test\\One\\'); $one_two_repo = $this->makeLocalRepo('test/two', '1.2.0', 'Test\\Two\\'); $this->makeWorkingDirectory(); $application = new Application(); $application->setAutoExit(false); $application->add(new InstallCommand()); $tester = new ApplicationTester($application); // Confirmation 1: install multiple dependencies $this->requirePackage('test/one 1.0.2'); $this->requirePackage('test/two 1.2.0'); $fs = new Filesystem(); // Confirmation 3: remove "vendor" folder $fs->removeDirectory($this->working_directory . '/vendor'); // Remove the repository of one of the dependencies to make it // unavailable. $fs->removeDirectory($one_two_repo); // Act $tester->run([ // Confirmation 4: install all dependencies 'command' => 'install', '-d' => $this->working_directory, ]); // Assert // Confirmation 2: System created composer.lock with dependencies and // version numbers. $this->assertPackageVersionLocked('test/one', '1.0.2'); $this->assertPackageVersionLocked('test/two', '1.2.0'); // Confirm that dependency installation failed and system communicated // this with its exit code and text output. $this->assertNotEquals(0, $tester->getStatusCode()); $this->assertMatchesRegularExpression( '/is[[:blank:]]+not[[:blank:]]+found[[:blank:]]+for[[:blank:]]+package[[:blank:]]+test\\/two/', $tester->getDisplay() ); } private function assertPackageVersionLocked(string $package, string $version): void { $lockfile = json_decode( file_get_contents($this->working_directory . '/composer.lock'), true ); $packages = $lockfile['packages']; $installed_package = null; foreach($packages as $package_lock) { if ($package_lock['name'] === $package) { $installed_package = $package_lock; } } $this->assertNotNull($installed_package); $this->assertEquals($version, $installed_package['version']); } private function assertPackageVersionInstalled(string $package, string $version): void { $install_directory = $this->working_directory . '/vendor/' . $package; $this->assertTrue(file_exists($install_directory)); $manifest = json_decode( file_get_contents($install_directory . '/composer.json'), true ); $this->assertEquals($version, $manifest['version']); } private function requirePackage(string $package): void { // Arrange $application = new Application(); $application->setAutoExit(false); $application->add(new RequireCommand()); $tester = new ApplicationTester($application); $tester->run([ 'command' => 'require', 'packages' => [ $package ], '-d' => $this->working_directory, ]); $output = $tester->getDisplay(); $tester->assertCommandIsSuccessful(); } private function makeLocalRepo(string $name, string $version, string $autoload): string { $local_repository = $this->getUniqueTmpDirectory(); $repo_manifest = [ 'name' => $name, 'version' => $version, 'type' => 'library', 'autoload' => [ 'psr-4' => [ $autoload => 'src/', ] ] ]; file_put_contents( $local_repository.'/composer.json', json_encode( $repo_manifest, JSON_UNESCAPED_SLASHES, ) ); $this->local_repositories[] = $local_repository; return $local_repository; } private function makeWorkingDirectory(): void { $this->working_directory = $this->getUniqueTmpDirectory(); $manifest = [ 'name' => 'test/something', 'require' => (object) null, 'repositories' => [ [ 'packagist.org' => false ] ] ]; foreach($this->local_repositories as $repository) { $manifest['repositories'][] = [ 'type' => 'path', 'url' => $repository, 'canonical' => false, ]; } file_put_contents( $this->working_directory.'/composer.json', json_encode($manifest, JSON_UNESCAPED_SLASHES) ); } }