From be0b99b7370f7a064a7e6ed62c784cfbd683de35 Mon Sep 17 00:00:00 2001
From: tedbow <tedbow@240860.no-reply.drupal.org>
Date: Sun, 11 Sep 2022 23:38:06 +0000
Subject: [PATCH] Issue #3308886 by tedbow: Consolidate Test fixture logic

---
 .../composer/installed.json}                  |  0
 .../composer/installed.json}                  |  0
 .../composer/installed.json}                  |  0
 .../composer/installed.json}                  |  0
 .../composer/installed.json}                  |  0
 .../composer/installed.json}                  |  0
 .../composer/installed.json}                  |  0
 .../composer/installed.json}                  |  0
 .../vendor/installed.json}                    |  0
 .../composer/installed.json}                  |  0
 ...tomaticUpdatesExtensionsKernelTestBase.php |  4 -
 .../tests/src/Kernel/ExtensionUpdaterTest.php |  3 -
 ...agesInstalledWithComposerValidatorTest.php |  3 +-
 .../Validator/UpdateReleaseValidatorTest.php  |  3 +-
 .../src/BypassedStagerServiceBase.php         |  5 +-
 .../tests/src/Kernel/ComposerUtilityTest.php  |  7 +-
 .../Kernel/PackageManagerKernelTestBase.php   | 72 +++++------------
 .../tests/src/Traits/FixtureUtilityTrait.php  | 79 +++++++++++++++++++
 .../vendor/composer/installed.json}           |  0
 .../vendor/composer/installed.json}           |  0
 .../vendor/composer/installed.json}           |  0
 .../vendor/composer/installed.json}           |  0
 .../vendor/composer/installed.json}           |  0
 .../vendor/composer/installed.json}           |  0
 .../vendor/composer/installed.json}           |  0
 .../vendor/composer/installed.json}           |  0
 tests/src/Build/UpdateTestBase.php            | 27 +------
 .../AutomaticUpdatesFunctionalTestBase.php    | 30 +------
 .../StagedProjectsValidatorTest.php           | 13 ++-
 29 files changed, 123 insertions(+), 123 deletions(-)
 rename automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/active/{active.installed.json => vendor/composer/installed.json} (100%)
 rename automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/module_not_installed_stage/{staged.installed.json => vendor/composer/installed.json} (100%)
 rename automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/module_theme_profile_dependency_not_installed_stage/{staged.installed.json => vendor/composer/installed.json} (100%)
 rename automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/profile_not_installed_stage/{staged.installed.json => vendor/composer/installed.json} (100%)
 rename automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/theme_not_installed_stage/{staged.installed.json => vendor/composer/installed.json} (100%)
 rename automatic_updates_extensions/tests/fixtures/update_release_validator/active/{active.installed.json => vendor/composer/installed.json} (100%)
 rename automatic_updates_extensions/tests/fixtures/update_release_validator/legacy_supported_update_stage/{staged.installed.json => vendor/composer/installed.json} (100%)
 rename automatic_updates_extensions/tests/fixtures/update_release_validator/legacy_unsupported_update_stage/{staged.installed.json => vendor/composer/installed.json} (100%)
 rename automatic_updates_extensions/tests/fixtures/update_release_validator/semver_supported_update_stage/{staged.installed.json => composer/vendor/installed.json} (100%)
 rename automatic_updates_extensions/tests/fixtures/update_release_validator/semver_unsupported_update_stage/{staged.installed.json => vendor/composer/installed.json} (100%)
 create mode 100644 package_manager/tests/src/Traits/FixtureUtilityTrait.php
 rename tests/fixtures/project_staged_validation/new_project_added/{active.installed.json => active/vendor/composer/installed.json} (100%)
 rename tests/fixtures/project_staged_validation/new_project_added/{staged.installed.json => staged/vendor/composer/installed.json} (100%)
 rename tests/fixtures/project_staged_validation/no_errors/{active.installed.json => active/vendor/composer/installed.json} (100%)
 rename tests/fixtures/project_staged_validation/no_errors/{staged.installed.json => staged/vendor/composer/installed.json} (100%)
 rename tests/fixtures/project_staged_validation/project_removed/{active.installed.json => active/vendor/composer/installed.json} (100%)
 rename tests/fixtures/project_staged_validation/project_removed/{staged.installed.json => staged/vendor/composer/installed.json} (100%)
 rename tests/fixtures/project_staged_validation/version_changed/{active.installed.json => active/vendor/composer/installed.json} (100%)
 rename tests/fixtures/project_staged_validation/version_changed/{staged.installed.json => staged/vendor/composer/installed.json} (100%)

diff --git a/automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/active/active.installed.json b/automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/active/vendor/composer/installed.json
similarity index 100%
rename from automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/active/active.installed.json
rename to automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/active/vendor/composer/installed.json
diff --git a/automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/module_not_installed_stage/staged.installed.json b/automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/module_not_installed_stage/vendor/composer/installed.json
similarity index 100%
rename from automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/module_not_installed_stage/staged.installed.json
rename to automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/module_not_installed_stage/vendor/composer/installed.json
diff --git a/automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/module_theme_profile_dependency_not_installed_stage/staged.installed.json b/automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/module_theme_profile_dependency_not_installed_stage/vendor/composer/installed.json
similarity index 100%
rename from automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/module_theme_profile_dependency_not_installed_stage/staged.installed.json
rename to automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/module_theme_profile_dependency_not_installed_stage/vendor/composer/installed.json
diff --git a/automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/profile_not_installed_stage/staged.installed.json b/automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/profile_not_installed_stage/vendor/composer/installed.json
similarity index 100%
rename from automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/profile_not_installed_stage/staged.installed.json
rename to automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/profile_not_installed_stage/vendor/composer/installed.json
diff --git a/automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/theme_not_installed_stage/staged.installed.json b/automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/theme_not_installed_stage/vendor/composer/installed.json
similarity index 100%
rename from automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/theme_not_installed_stage/staged.installed.json
rename to automatic_updates_extensions/tests/fixtures/packages_installed_with_composer_validator/theme_not_installed_stage/vendor/composer/installed.json
diff --git a/automatic_updates_extensions/tests/fixtures/update_release_validator/active/active.installed.json b/automatic_updates_extensions/tests/fixtures/update_release_validator/active/vendor/composer/installed.json
similarity index 100%
rename from automatic_updates_extensions/tests/fixtures/update_release_validator/active/active.installed.json
rename to automatic_updates_extensions/tests/fixtures/update_release_validator/active/vendor/composer/installed.json
diff --git a/automatic_updates_extensions/tests/fixtures/update_release_validator/legacy_supported_update_stage/staged.installed.json b/automatic_updates_extensions/tests/fixtures/update_release_validator/legacy_supported_update_stage/vendor/composer/installed.json
similarity index 100%
rename from automatic_updates_extensions/tests/fixtures/update_release_validator/legacy_supported_update_stage/staged.installed.json
rename to automatic_updates_extensions/tests/fixtures/update_release_validator/legacy_supported_update_stage/vendor/composer/installed.json
diff --git a/automatic_updates_extensions/tests/fixtures/update_release_validator/legacy_unsupported_update_stage/staged.installed.json b/automatic_updates_extensions/tests/fixtures/update_release_validator/legacy_unsupported_update_stage/vendor/composer/installed.json
similarity index 100%
rename from automatic_updates_extensions/tests/fixtures/update_release_validator/legacy_unsupported_update_stage/staged.installed.json
rename to automatic_updates_extensions/tests/fixtures/update_release_validator/legacy_unsupported_update_stage/vendor/composer/installed.json
diff --git a/automatic_updates_extensions/tests/fixtures/update_release_validator/semver_supported_update_stage/staged.installed.json b/automatic_updates_extensions/tests/fixtures/update_release_validator/semver_supported_update_stage/composer/vendor/installed.json
similarity index 100%
rename from automatic_updates_extensions/tests/fixtures/update_release_validator/semver_supported_update_stage/staged.installed.json
rename to automatic_updates_extensions/tests/fixtures/update_release_validator/semver_supported_update_stage/composer/vendor/installed.json
diff --git a/automatic_updates_extensions/tests/fixtures/update_release_validator/semver_unsupported_update_stage/staged.installed.json b/automatic_updates_extensions/tests/fixtures/update_release_validator/semver_unsupported_update_stage/vendor/composer/installed.json
similarity index 100%
rename from automatic_updates_extensions/tests/fixtures/update_release_validator/semver_unsupported_update_stage/staged.installed.json
rename to automatic_updates_extensions/tests/fixtures/update_release_validator/semver_unsupported_update_stage/vendor/composer/installed.json
diff --git a/automatic_updates_extensions/tests/src/Kernel/AutomaticUpdatesExtensionsKernelTestBase.php b/automatic_updates_extensions/tests/src/Kernel/AutomaticUpdatesExtensionsKernelTestBase.php
index a6357c9c77..04bc6d7342 100644
--- a/automatic_updates_extensions/tests/src/Kernel/AutomaticUpdatesExtensionsKernelTestBase.php
+++ b/automatic_updates_extensions/tests/src/Kernel/AutomaticUpdatesExtensionsKernelTestBase.php
@@ -8,15 +8,12 @@ use Drupal\package_manager\Exception\StageValidationException;
 use Drupal\Tests\automatic_updates\Kernel\AutomaticUpdatesKernelTestBase;
 use Drupal\Tests\package_manager\Kernel\TestPathFactory;
 use Drupal\Tests\package_manager\Kernel\TestStageTrait;
-use Drupal\Tests\package_manager\Traits\InfoYmlConverterTrait;
 
 /**
  * Base class for kernel tests of the Automatic Updates Extensions module.
  */
 abstract class AutomaticUpdatesExtensionsKernelTestBase extends AutomaticUpdatesKernelTestBase {
 
-  use InfoYmlConverterTrait;
-
   /**
    * {@inheritdoc}
    */
@@ -47,7 +44,6 @@ abstract class AutomaticUpdatesExtensionsKernelTestBase extends AutomaticUpdates
   protected function createVirtualProject(?string $source_dir = NULL): void {
     $source_dir = $source_dir ?? __DIR__ . '/../../fixtures/fake-site';
     parent::createVirtualProject($source_dir);
-    $this->renameVfsInfoYmlFiles();
   }
 
   /**
diff --git a/automatic_updates_extensions/tests/src/Kernel/ExtensionUpdaterTest.php b/automatic_updates_extensions/tests/src/Kernel/ExtensionUpdaterTest.php
index b052627084..3f9040ce4d 100644
--- a/automatic_updates_extensions/tests/src/Kernel/ExtensionUpdaterTest.php
+++ b/automatic_updates_extensions/tests/src/Kernel/ExtensionUpdaterTest.php
@@ -3,7 +3,6 @@
 namespace Drupal\Tests\automatic_updates_extensions\Kernel;
 
 use Drupal\Tests\automatic_updates\Kernel\AutomaticUpdatesKernelTestBase;
-use Drupal\Tests\package_manager\Traits\InfoYmlConverterTrait;
 use Drupal\Tests\user\Traits\UserCreationTrait;
 
 /**
@@ -14,7 +13,6 @@ use Drupal\Tests\user\Traits\UserCreationTrait;
 class ExtensionUpdaterTest extends AutomaticUpdatesKernelTestBase {
 
   use UserCreationTrait;
-  use InfoYmlConverterTrait;
 
   /**
    * {@inheritdoc}
@@ -42,7 +40,6 @@ class ExtensionUpdaterTest extends AutomaticUpdatesKernelTestBase {
     $this->setCurrentUser($user);
 
     $this->createVirtualProject(__DIR__ . '/../../fixtures/fake-site');
-    $this->renameVfsInfoYmlFiles();
   }
 
   /**
diff --git a/automatic_updates_extensions/tests/src/Kernel/Validator/PackagesInstalledWithComposerValidatorTest.php b/automatic_updates_extensions/tests/src/Kernel/Validator/PackagesInstalledWithComposerValidatorTest.php
index 1ed0f01116..ae1d8f67fb 100644
--- a/automatic_updates_extensions/tests/src/Kernel/Validator/PackagesInstalledWithComposerValidatorTest.php
+++ b/automatic_updates_extensions/tests/src/Kernel/Validator/PackagesInstalledWithComposerValidatorTest.php
@@ -85,7 +85,8 @@ class PackagesInstalledWithComposerValidatorTest extends AutomaticUpdatesExtensi
    */
   public function testPreApplyException(string $stage_dir, array $expected_results): void {
     $active_dir = __DIR__ . '/../../../fixtures/packages_installed_with_composer_validator/active';
-    $this->useComposerFixturesFiles($active_dir, $stage_dir);
+    $this->copyFixtureFolderToActiveDirectory($active_dir);
+    $this->copyFixtureFolderToStageDirectoryOnApply($stage_dir);
     $this->assertUpdateResults(['my_module' => '9.8.1'], $expected_results, PreApplyEvent::class);
   }
 
diff --git a/automatic_updates_extensions/tests/src/Kernel/Validator/UpdateReleaseValidatorTest.php b/automatic_updates_extensions/tests/src/Kernel/Validator/UpdateReleaseValidatorTest.php
index d53e553b01..5d8d11c76d 100644
--- a/automatic_updates_extensions/tests/src/Kernel/Validator/UpdateReleaseValidatorTest.php
+++ b/automatic_updates_extensions/tests/src/Kernel/Validator/UpdateReleaseValidatorTest.php
@@ -131,7 +131,8 @@ class UpdateReleaseValidatorTest extends AutomaticUpdatesExtensionsKernelTestBas
       'drupal' => __DIR__ . '/../../../../../tests/fixtures/release-history/drupal.9.8.2.xml',
     ]);
     $active_dir = __DIR__ . '/../../../fixtures/update_release_validator/active';
-    $this->useComposerFixturesFiles($active_dir, $stage_dir);
+    $this->copyFixtureFolderToActiveDirectory($active_dir);
+    $this->copyFixtureFolderToStageDirectoryOnApply($stage_dir);
     if ($error_expected) {
       $expected_results = [
         ValidationResult::createError(
diff --git a/package_manager/tests/modules/package_manager_bypass/src/BypassedStagerServiceBase.php b/package_manager/tests/modules/package_manager_bypass/src/BypassedStagerServiceBase.php
index b64b3be063..4457fcf4e9 100644
--- a/package_manager/tests/modules/package_manager_bypass/src/BypassedStagerServiceBase.php
+++ b/package_manager/tests/modules/package_manager_bypass/src/BypassedStagerServiceBase.php
@@ -58,6 +58,9 @@ abstract class BypassedStagerServiceBase {
   /**
    * If a fixture path has been set, mirrors it to the given path.
    *
+   * Files in the destination directory but not in the source directory will
+   * not be deleted.
+   *
    * @param \PhpTuf\ComposerStager\Domain\Value\Path\PathInterface $destination
    *   The path to which the fixture files should be mirrored.
    */
@@ -67,7 +70,7 @@ abstract class BypassedStagerServiceBase {
     if ($fixture_path && is_dir($fixture_path)) {
       $this->fileSystem->mirror($fixture_path, $destination->resolve(), NULL, [
         'override' => TRUE,
-        'delete' => TRUE,
+        'delete' => FALSE,
       ]);
     }
   }
diff --git a/package_manager/tests/src/Kernel/ComposerUtilityTest.php b/package_manager/tests/src/Kernel/ComposerUtilityTest.php
index 7741b0b634..fa4e1d1006 100644
--- a/package_manager/tests/src/Kernel/ComposerUtilityTest.php
+++ b/package_manager/tests/src/Kernel/ComposerUtilityTest.php
@@ -4,7 +4,7 @@ namespace Drupal\Tests\package_manager\Kernel;
 
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\package_manager\ComposerUtility;
-use Drupal\Tests\package_manager\Traits\InfoYmlConverterTrait;
+use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait;
 use org\bovigo\vfs\vfsStream;
 
 /**
@@ -14,7 +14,7 @@ use org\bovigo\vfs\vfsStream;
  */
 class ComposerUtilityTest extends KernelTestBase {
 
-  use InfoYmlConverterTrait;
+  use FixtureUtilityTrait;
 
   /**
    * {@inheritdoc}
@@ -28,9 +28,8 @@ class ComposerUtilityTest extends KernelTestBase {
     parent::setUp();
 
     $fixture = vfsStream::newDirectory('fixture');
-    vfsStream::copyFromFileSystem(__DIR__ . '/../../fixtures/project_package_conversion', $fixture);
     $this->vfsRoot->addChild($fixture);
-    $this->renameVfsInfoYmlFiles();
+    static::copyFixtureFilesTo(__DIR__ . '/../../fixtures/project_package_conversion', $fixture->url());
   }
 
   /**
diff --git a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
index 280bb13327..e9490304df 100644
--- a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
+++ b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
@@ -12,6 +12,7 @@ use Drupal\package_manager\Exception\StageException;
 use Drupal\package_manager\Exception\StageValidationException;
 use Drupal\package_manager\Stage;
 use Drupal\package_manager_bypass\Beginner;
+use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait;
 use Drupal\Tests\package_manager\Traits\ValidationTestTrait;
 use GuzzleHttp\Client;
 use GuzzleHttp\Handler\MockHandler;
@@ -19,9 +20,6 @@ use GuzzleHttp\HandlerStack;
 use GuzzleHttp\Psr7\Response;
 use GuzzleHttp\Psr7\Utils;
 use org\bovigo\vfs\vfsStream;
-use org\bovigo\vfs\vfsStreamDirectory;
-use org\bovigo\vfs\vfsStreamFile;
-use org\bovigo\vfs\visitor\vfsStreamAbstractVisitor;
 use PhpTuf\ComposerStager\Domain\Value\Path\PathInterface;
 use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
 use PhpTuf\ComposerStager\Infrastructure\Value\Path\AbstractPath;
@@ -33,6 +31,7 @@ use Symfony\Component\DependencyInjection\Definition;
  */
 abstract class PackageManagerKernelTestBase extends KernelTestBase {
 
+  use FixtureUtilityTrait;
   use ValidationTestTrait;
 
   /**
@@ -218,31 +217,8 @@ abstract class PackageManagerKernelTestBase extends KernelTestBase {
     // Create the active directory and copy its contents from a fixture.
     $active_dir = vfsStream::newDirectory('active');
     $this->vfsRoot->addChild($active_dir);
-    vfsStream::copyFromFileSystem($source_dir, $active_dir);
-
-    // Because we can't commit physical `.git` directories into the fixture, use
-    // a visitor to traverse the virtual file system and rename all `_git`
-    // directories to `.git`.
-    vfsStream::inspect(new class () extends vfsStreamAbstractVisitor {
-
-      /**
-       * {@inheritdoc}
-       */
-      public function visitFile(vfsStreamFile $file) {}
-
-      /**
-       * {@inheritdoc}
-       */
-      public function visitDirectory(vfsStreamDirectory $dir) {
-        if ($dir->getName() === '_git') {
-          $dir->rename('.git');
-        }
-        foreach ($dir->getChildren() as $child) {
-          $this->visit($child);
-        }
-      }
-
-    });
+    $active_dir = $active_dir->url();
+    static::copyFixtureFilesTo($source_dir, $active_dir);
 
     // Create a staging root directory alongside the active directory.
     $stage_dir = vfsStream::newDirectory('stage');
@@ -251,7 +227,6 @@ abstract class PackageManagerKernelTestBase extends KernelTestBase {
     // Ensure the path locator points to the virtual active directory. We assume
     // that is its own web root and that the vendor directory is at its top
     // level.
-    $active_dir = $active_dir->url();
     /** @var \Drupal\package_manager_bypass\PathLocator $path_locator */
     $path_locator = $this->container->get('package_manager.path_locator');
     $path_locator->setPaths($active_dir, $active_dir . '/vendor', '', $stage_dir->url());
@@ -279,36 +254,31 @@ abstract class PackageManagerKernelTestBase extends KernelTestBase {
   }
 
   /**
-   * Copies composer fixture files to active and stage directories.
+   * Copies a fixture directory into the active directory.
    *
    * @param string $active_fixture_dir
    *   Path to fixture active directory from which the files will be copied.
-   * @param string|null $stage_fixture_dir
-   *   (optional) Path to fixture staged directory from which the files will be
-   *    copied. If not provided $active_dir_to_set will be used as the fixture
-   *    staged directory also.
    */
-  protected function useComposerFixturesFiles(string $active_fixture_dir, string $stage_fixture_dir = NULL) {
-    $this->assertFileIsReadable("$active_fixture_dir/active.installed.json");
+  protected function copyFixtureFolderToActiveDirectory(string $active_fixture_dir) {
     $active_dir = $this->container->get('package_manager.path_locator')
       ->getProjectRoot();
-    copy("$active_fixture_dir/active.installed.json", "$active_dir/vendor/composer/installed.json");
+    static::copyFixtureFilesTo($active_fixture_dir, $active_dir);
+  }
 
-    // Before any other pre-apply listener runs, replaced the staged
-    // `vendor/composer/installed.json` with the fixture's
-    // `staged.installed.json`.
-    if ($stage_fixture_dir === NULL) {
-      $stage_fixture_dir = $active_fixture_dir;
-    }
-    if (!file_exists("$stage_fixture_dir/staged.installed.json")) {
-      return;
-    }
-    $this->assertFileIsReadable("$stage_fixture_dir/staged.installed.json");
-    $listener = function (PreApplyEvent $event) use ($stage_fixture_dir): void {
-      copy("$stage_fixture_dir/staged.installed.json", $event->getStage()->getStageDirectory() . "/vendor/composer/installed.json");
+  /**
+   * Copies a fixture directory into the stage directory on apply.
+   *
+   * @param string $fixture_dir
+   *   Path to fixture directory from which the files will be copied.
+   */
+  protected function copyFixtureFolderToStageDirectoryOnApply(string $fixture_dir) {
+    /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher */
+    $event_dispatcher = $this->container->get('event_dispatcher');
+
+    $listener = function (PreApplyEvent $event) use ($fixture_dir): void {
+      static::copyFixtureFilesTo($fixture_dir, $event->getStage()->getStageDirectory());
     };
-    $this->container->get('event_dispatcher')
-      ->addListener(PreApplyEvent::class, $listener, PHP_INT_MAX);
+    $event_dispatcher->addListener(PreApplyEvent::class, $listener, PHP_INT_MAX);
   }
 
   /**
diff --git a/package_manager/tests/src/Traits/FixtureUtilityTrait.php b/package_manager/tests/src/Traits/FixtureUtilityTrait.php
new file mode 100644
index 0000000000..e7b66acb6b
--- /dev/null
+++ b/package_manager/tests/src/Traits/FixtureUtilityTrait.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Drupal\Tests\package_manager\Traits;
+
+use Symfony\Component\Filesystem\Filesystem;
+use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator;
+
+/**
+ * A utility for all things fixtures.
+ */
+trait FixtureUtilityTrait {
+
+  /**
+   * Mirrors a fixture directory to the given path.
+   *
+   * Files not in the source fixture directory will not be deleted from
+   * destination directory. After copying the files to the destination directory
+   * the files and folders will be converted so that can be used in the tests.
+   * The conversion includes:
+   * - Renaming '_git' directories to '.git'
+   * - Renaming files ending in '.info.yml.hide' to remove '.hide'.
+   *
+   * @param string $source_path
+   *   The source path.
+   * @param string $destination_path
+   *   The path to which the fixture files should be mirrored.
+   */
+  protected static function copyFixtureFilesTo(string $source_path, string $destination_path): void {
+    (new Filesystem())->mirror($source_path, $destination_path, NULL, [
+      'override' => TRUE,
+      'delete' => FALSE,
+    ]);
+    static::renameInfoYmlFiles($destination_path);
+    static::renameGitDirectories($destination_path);
+  }
+
+  /**
+   * Renames all files that end with .info.yml.hide.
+   *
+   * @param string $dir
+   *   The directory to be iterated through.
+   */
+  protected static function renameInfoYmlFiles(string $dir) {
+    // Construct the iterator.
+    $it = new RecursiveDirectoryIterator($dir, \RecursiveIteratorIterator::SELF_FIRST);
+
+    // Loop through files and rename them.
+    foreach (new \RecursiveIteratorIterator($it) as $file) {
+      if ($file->getExtension() == 'hide') {
+        rename($file->getPathname(), $dir . DIRECTORY_SEPARATOR .
+          $file->getRelativePath() . DIRECTORY_SEPARATOR . str_replace(".hide", "", $file->getFilename()));
+      }
+    }
+  }
+
+  /**
+   * Renames _git directories to .git.
+   *
+   * @param string $dir
+   *   The directory to be iterated through.
+   */
+  private static function renameGitDirectories(string $dir) {
+    $iter = new \RecursiveIteratorIterator(
+      new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
+      \RecursiveIteratorIterator::SELF_FIRST,
+      \RecursiveIteratorIterator::CATCH_GET_CHILD
+    );
+    /** @var \Symfony\Component\Finder\SplFileInfo $file */
+    foreach ($iter as $file) {
+      if ($file->isDir() && $file->getFilename() === '_git' && $file->getRelativePathname()) {
+        rename(
+          $file->getPathname(),
+          $file->getPath() . DIRECTORY_SEPARATOR . '.git'
+        );
+      }
+    }
+  }
+
+}
diff --git a/tests/fixtures/project_staged_validation/new_project_added/active.installed.json b/tests/fixtures/project_staged_validation/new_project_added/active/vendor/composer/installed.json
similarity index 100%
rename from tests/fixtures/project_staged_validation/new_project_added/active.installed.json
rename to tests/fixtures/project_staged_validation/new_project_added/active/vendor/composer/installed.json
diff --git a/tests/fixtures/project_staged_validation/new_project_added/staged.installed.json b/tests/fixtures/project_staged_validation/new_project_added/staged/vendor/composer/installed.json
similarity index 100%
rename from tests/fixtures/project_staged_validation/new_project_added/staged.installed.json
rename to tests/fixtures/project_staged_validation/new_project_added/staged/vendor/composer/installed.json
diff --git a/tests/fixtures/project_staged_validation/no_errors/active.installed.json b/tests/fixtures/project_staged_validation/no_errors/active/vendor/composer/installed.json
similarity index 100%
rename from tests/fixtures/project_staged_validation/no_errors/active.installed.json
rename to tests/fixtures/project_staged_validation/no_errors/active/vendor/composer/installed.json
diff --git a/tests/fixtures/project_staged_validation/no_errors/staged.installed.json b/tests/fixtures/project_staged_validation/no_errors/staged/vendor/composer/installed.json
similarity index 100%
rename from tests/fixtures/project_staged_validation/no_errors/staged.installed.json
rename to tests/fixtures/project_staged_validation/no_errors/staged/vendor/composer/installed.json
diff --git a/tests/fixtures/project_staged_validation/project_removed/active.installed.json b/tests/fixtures/project_staged_validation/project_removed/active/vendor/composer/installed.json
similarity index 100%
rename from tests/fixtures/project_staged_validation/project_removed/active.installed.json
rename to tests/fixtures/project_staged_validation/project_removed/active/vendor/composer/installed.json
diff --git a/tests/fixtures/project_staged_validation/project_removed/staged.installed.json b/tests/fixtures/project_staged_validation/project_removed/staged/vendor/composer/installed.json
similarity index 100%
rename from tests/fixtures/project_staged_validation/project_removed/staged.installed.json
rename to tests/fixtures/project_staged_validation/project_removed/staged/vendor/composer/installed.json
diff --git a/tests/fixtures/project_staged_validation/version_changed/active.installed.json b/tests/fixtures/project_staged_validation/version_changed/active/vendor/composer/installed.json
similarity index 100%
rename from tests/fixtures/project_staged_validation/version_changed/active.installed.json
rename to tests/fixtures/project_staged_validation/version_changed/active/vendor/composer/installed.json
diff --git a/tests/fixtures/project_staged_validation/version_changed/staged.installed.json b/tests/fixtures/project_staged_validation/version_changed/staged/vendor/composer/installed.json
similarity index 100%
rename from tests/fixtures/project_staged_validation/version_changed/staged.installed.json
rename to tests/fixtures/project_staged_validation/version_changed/staged/vendor/composer/installed.json
diff --git a/tests/src/Build/UpdateTestBase.php b/tests/src/Build/UpdateTestBase.php
index 32d754d758..c88d11f40a 100644
--- a/tests/src/Build/UpdateTestBase.php
+++ b/tests/src/Build/UpdateTestBase.php
@@ -4,15 +4,15 @@ namespace Drupal\Tests\automatic_updates\Build;
 
 use Drupal\Component\Utility\Html;
 use Drupal\Tests\package_manager\Build\TemplateProjectTestBase;
+use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait;
 use Drupal\Tests\RandomGeneratorTrait;
-use Symfony\Component\Filesystem\Filesystem;
-use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator;
 
 /**
  * Base class for tests that perform in-place updates.
  */
 abstract class UpdateTestBase extends TemplateProjectTestBase {
 
+  use FixtureUtilityTrait;
   use RandomGeneratorTrait;
 
   /**
@@ -117,29 +117,8 @@ END;
    */
   protected function copyFixtureToTempDirectory(string $fixture_directory): string {
     $temp_directory = $this->getWorkspaceDirectory() . '/fixtures_temp_' . $this->randomMachineName(20);
-    (new Filesystem())->mirror($fixture_directory, $temp_directory);
-    $this->assertDirectoryIsWritable($temp_directory);
-    $this->renameInfoYmlFiles($temp_directory);
+    static::copyFixtureFilesTo($fixture_directory, $temp_directory);
     return $temp_directory;
   }
 
-  /**
-   * Renames all files that end with .info.yml.hide.
-   *
-   * @param string $dir
-   *   The directory to be iterated through.
-   */
-  protected function renameInfoYmlFiles(string $dir) {
-    // Construct the iterator.
-    $it = new RecursiveDirectoryIterator($dir, \RecursiveIteratorIterator::SELF_FIRST);
-
-    // Loop through files and rename them.
-    foreach (new \RecursiveIteratorIterator($it) as $file) {
-      if ($file->getExtension() == 'hide') {
-        rename($file->getPathname(), $dir . DIRECTORY_SEPARATOR .
-            $file->getRelativePath() . DIRECTORY_SEPARATOR . str_replace(".hide", "", $file->getFilename()));
-      }
-    }
-  }
-
 }
diff --git a/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php b/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php
index 7b2cc2216c..b44e9f7edd 100644
--- a/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php
+++ b/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php
@@ -6,15 +6,15 @@ use Drupal\Core\Site\Settings;
 use Drupal\package_manager_bypass\Beginner;
 use Drupal\package_manager_bypass\Stager;
 use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\Filesystem\Filesystem;
-use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator;
 
 /**
  * Base class for functional tests of the Automatic Updates module.
  */
 abstract class AutomaticUpdatesFunctionalTestBase extends BrowserTestBase {
 
+  use FixtureUtilityTrait;
   /**
    * {@inheritdoc}
    */
@@ -174,8 +174,7 @@ abstract class AutomaticUpdatesFunctionalTestBase extends BrowserTestBase {
    */
   protected function copyFixtureToTempDirectory(string $fixture_directory): string {
     $temp_directory = $this->root . DIRECTORY_SEPARATOR . $this->siteDirectory . DIRECTORY_SEPARATOR . $this->randomMachineName(20);
-    (new Filesystem())->mirror($fixture_directory, $temp_directory);
-    $this->assertDirectoryIsWritable($temp_directory);
+    static::copyFixtureFilesTo($fixture_directory, $temp_directory);
     return $temp_directory;
   }
 
@@ -193,8 +192,6 @@ abstract class AutomaticUpdatesFunctionalTestBase extends BrowserTestBase {
     Beginner::setFixturePath($active_dir);
     $this->container->get('package_manager.path_locator')
       ->setPaths($active_dir, $active_dir . '/vendor', '', NULL);
-
-    $this->renameInfoYmlFiles($active_dir);
   }
 
   /**
@@ -209,27 +206,6 @@ abstract class AutomaticUpdatesFunctionalTestBase extends BrowserTestBase {
     // directory and not affect other tests.
     $staged_dir = $this->copyFixtureToTempDirectory($fixture_directory);
     Stager::setFixturePath($staged_dir);
-
-    $this->renameInfoYmlFiles($staged_dir);
-  }
-
-  /**
-   * Renames all files that end with .info.yml.hide.
-   *
-   * @param string $dir
-   *   The directory to be iterated through.
-   */
-  protected function renameInfoYmlFiles(string $dir) {
-    // Construct the iterator.
-    $it = new RecursiveDirectoryIterator($dir, \RecursiveIteratorIterator::SELF_FIRST);
-
-    // Loop through files and rename them.
-    foreach (new \RecursiveIteratorIterator($it) as $file) {
-      if ($file->getExtension() == 'hide') {
-        rename($file->getPathname(), $dir . DIRECTORY_SEPARATOR . $file->getRelativePath() . DIRECTORY_SEPARATOR . str_replace(".hide", "", $file->getFilename()));
-      }
-    }
-
   }
 
 }
diff --git a/tests/src/Kernel/ReadinessValidation/StagedProjectsValidatorTest.php b/tests/src/Kernel/ReadinessValidation/StagedProjectsValidatorTest.php
index 46a8da6861..65193655df 100644
--- a/tests/src/Kernel/ReadinessValidation/StagedProjectsValidatorTest.php
+++ b/tests/src/Kernel/ReadinessValidation/StagedProjectsValidatorTest.php
@@ -87,11 +87,9 @@ class StagedProjectsValidatorTest extends AutomaticUpdatesKernelTestBase {
   /**
    * Tests validation errors, or lack thereof.
    *
-   * @param string $fixtures_dir
-   *   A directory containing `active.installed.json` and
-   *   `staged.installed.json` files. These will be used as the virtual
-   *   project's active and staged `vendor/composer/installed.json` files,
-   *   respectively.
+   * @param string $root_fixture_directory
+   *   A directory containing to fixtures sub direcotories, 'active' and
+   *   'staged'.
    * @param string|null $expected_summary
    *   The expected error summary, or NULL if no errors are expected.
    * @param string[] $expected_messages
@@ -99,8 +97,9 @@ class StagedProjectsValidatorTest extends AutomaticUpdatesKernelTestBase {
    *
    * @dataProvider providerErrors
    */
-  public function testErrors(string $fixtures_dir, ?string $expected_summary, array $expected_messages): void {
-    $this->useComposerFixturesFiles($fixtures_dir);
+  public function testErrors(string $root_fixture_directory, ?string $expected_summary, array $expected_messages): void {
+    $this->copyFixtureFolderToActiveDirectory("$root_fixture_directory/active");
+    $this->copyFixtureFolderToStageDirectoryOnApply("$root_fixture_directory/staged");
 
     $expected_results = [];
     if ($expected_messages) {
-- 
GitLab