From cbbe53da9a1d0317ca7f75c5027e75118e8cb0f9 Mon Sep 17 00:00:00 2001 From: catch <catch@35733.no-reply.drupal.org> Date: Mon, 18 Dec 2023 17:07:38 +0000 Subject: [PATCH] Issue #3403382 by alexpott, Wim Leers: BuildTestBase makes assumptions it should not about the code layout --- .../BuildTests/Framework/BuildTestBase.php | 64 ++++++++++++-- .../Framework/Tests/BuildTestTest.php | 88 ++++++++++++++++++- 2 files changed, 141 insertions(+), 11 deletions(-) diff --git a/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php b/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php index f566064807d5..408143aeaa55 100644 --- a/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php +++ b/core/tests/Drupal/BuildTests/Framework/BuildTestBase.php @@ -7,6 +7,7 @@ use Behat\Mink\Driver\BrowserKitDriver; use Behat\Mink\Mink; use Behat\Mink\Session; +use Composer\InstalledVersions; use Drupal\Component\FileSystem\FileSystem as DrupalFilesystem; use Drupal\Tests\DrupalTestBrowser; use Drupal\Tests\PhpUnitCompatibilityTrait; @@ -557,7 +558,7 @@ public function copyCodebase(\Iterator $iterator = NULL, $working_dir = NULL) { $fs = new SymfonyFilesystem(); $options = ['override' => TRUE, 'delete' => FALSE]; - $fs->mirror($this->getDrupalRoot(), $working_path, $iterator, $options); + $fs->mirror($this->getComposerRoot(), $working_path, $iterator, $options); } /** @@ -577,14 +578,16 @@ public function copyCodebase(\Iterator $iterator = NULL, $working_dir = NULL) { * A Finder object ready to iterate over core codebase. */ public function getCodebaseFinder() { + $drupal_root = $this->getWorkingPathDrupalRoot() ?? ''; $finder = new Finder(); $finder->files() + ->followLinks() ->ignoreUnreadableDirs() - ->in($this->getDrupalRoot()) - ->notPath('#^sites/default/files#') - ->notPath('#^sites/simpletest#') - ->notPath('#^core/node_modules#') - ->notPath('#^sites/default/settings\..*php#') + ->in($this->getComposerRoot()) + ->notPath("#^{$drupal_root}sites/default/files#") + ->notPath("#^{$drupal_root}sites/simpletest#") + ->notPath("#^{$drupal_root}core/node_modules#") + ->notPath("#^{$drupal_root}sites/default/settings\..*php#") ->ignoreDotFiles(FALSE) ->ignoreVCS(FALSE); return $finder; @@ -596,8 +599,53 @@ public function getCodebaseFinder() { * @return string * The full path to the root of this Drupal codebase. */ - protected function getDrupalRoot() { - return realpath(dirname(__DIR__, 5)); + public function getDrupalRoot() { + // Given this code is in the drupal/core package, $core cannot be NULL. + /** @var string $core */ + $core = InstalledVersions::getInstallPath('drupal/core'); + return realpath(dirname($core)); + } + + /** + * Gets the path to the Composer root directory. + * + * @return string + * The absolute path to the Composer root directory. + */ + public function getComposerRoot(): string { + $root = InstalledVersions::getRootPackage(); + return realpath($root['install_path']); + } + + /** + * Gets the path to Drupal root in the workspace directory. + * + * @return string + * The absolute path to the Drupal root directory in the workspace. + */ + public function getWorkspaceDrupalRoot(): string { + $dir = $this->getWorkspaceDirectory(); + $drupal_root = $this->getWorkingPathDrupalRoot(); + if ($drupal_root !== NULL) { + $dir = $dir . DIRECTORY_SEPARATOR . $drupal_root; + } + return $dir; + } + + /** + * Gets the working path for Drupal core. + * + * @return string|null + * The relative path to Drupal's root directory or NULL if it is the same + * as the composer root directory. + */ + public function getWorkingPathDrupalRoot(): ?string { + $composer_root = $this->getComposerRoot(); + $drupal_root = $this->getDrupalRoot(); + if ($composer_root === $drupal_root) { + return NULL; + } + return (new SymfonyFilesystem())->makePathRelative($drupal_root, $composer_root); } } diff --git a/core/tests/Drupal/BuildTests/Framework/Tests/BuildTestTest.php b/core/tests/Drupal/BuildTests/Framework/Tests/BuildTestTest.php index d58fdacc5fb7..c55ae1580ebd 100644 --- a/core/tests/Drupal/BuildTests/Framework/Tests/BuildTestTest.php +++ b/core/tests/Drupal/BuildTests/Framework/Tests/BuildTestTest.php @@ -87,14 +87,18 @@ public function testCopyCodebaseExclude() { ], ]); - // Mock BuildTestBase so that it thinks our VFS is the Drupal root. + // Mock BuildTestBase so that it thinks our VFS is the Composer and Drupal + // roots. /** @var \PHPUnit\Framework\MockObject\MockBuilder|\Drupal\BuildTests\Framework\BuildTestBase $base */ $base = $this->getMockBuilder(BuildTestBase::class) - ->onlyMethods(['getDrupalRoot']) + ->onlyMethods(['getDrupalRoot', 'getComposerRoot']) ->getMockForAbstractClass(); - $base->expects($this->exactly(2)) + $base->expects($this->exactly(1)) ->method('getDrupalRoot') ->willReturn(vfsStream::url('drupal')); + $base->expects($this->exactly(3)) + ->method('getComposerRoot') + ->willReturn(vfsStream::url('drupal')); $base->setUp(); @@ -121,6 +125,84 @@ public function testCopyCodebaseExclude() { $base->tearDown(); } + /** + * Tests copying codebase when Drupal and Composer roots are different. + * + * @covers ::copyCodebase + */ + public function testCopyCodebaseDocRoot() { + // Create a virtual file system containing items that should be + // excluded. Exception being modules directory. + vfsStream::setup('drupal', NULL, [ + 'docroot' => [ + 'sites' => [ + 'default' => [ + 'files' => [ + 'a_file.txt' => 'some file.', + ], + 'settings.php' => '<?php $settings = "stuff";', + 'settings.local.php' => '<?php $settings = "override";', + 'default.settings.php' => '<?php $settings = "default";', + ], + 'simpletest' => [ + 'simpletest_hash' => [ + 'some_results.xml' => '<xml/>', + ], + ], + ], + 'modules' => [ + 'my_module' => [ + 'vendor' => [ + 'my_vendor' => [ + 'composer.json' => "{\n}", + ], + ], + ], + ], + ], + 'vendor' => [ + 'test.txt' => 'File exists', + ], + ]); + + // Mock BuildTestBase so that it thinks our VFS is the Composer and Drupal + // roots. + /** @var \PHPUnit\Framework\MockObject\MockBuilder|\Drupal\BuildTests\Framework\BuildTestBase $base */ + $base = $this->getMockBuilder(BuildTestBase::class) + ->onlyMethods(['getDrupalRoot', 'getComposerRoot']) + ->getMockForAbstractClass(); + $base->expects($this->exactly(3)) + ->method('getDrupalRoot') + ->willReturn(vfsStream::url('drupal/docroot')); + $base->expects($this->exactly(5)) + ->method('getComposerRoot') + ->willReturn(vfsStream::url('drupal')); + + $base->setUp(); + + // Perform the copy. + $base->copyCodebase(); + $full_path = $base->getWorkspaceDirectory(); + + $this->assertDirectoryExists($full_path . '/docroot'); + + // Verify expected files exist. + $this->assertFileExists($full_path . DIRECTORY_SEPARATOR . 'docroot/modules/my_module/vendor/my_vendor/composer.json'); + $this->assertFileExists($full_path . DIRECTORY_SEPARATOR . 'docroot/sites/default/default.settings.php'); + $this->assertFileExists($full_path . DIRECTORY_SEPARATOR . 'vendor'); + + // Verify expected files do not exist + $this->assertFileDoesNotExist($full_path . DIRECTORY_SEPARATOR . 'docroot/sites/default/settings.php'); + $this->assertFileDoesNotExist($full_path . DIRECTORY_SEPARATOR . 'docroot/sites/default/settings.local.php'); + $this->assertFileDoesNotExist($full_path . DIRECTORY_SEPARATOR . 'docroot/sites/default/files'); + + // Ensure that the workspace Drupal root is calculated correctly. + $this->assertSame($full_path . '/docroot/', $base->getWorkspaceDrupalRoot()); + $this->assertSame('docroot/', $base->getWorkingPathDrupalRoot()); + + $base->tearDown(); + } + /** * @covers ::findAvailablePort */ -- GitLab