diff --git a/core/tests/Drupal/Tests/Component/Scaffold/Fixtures.php b/core/tests/Drupal/Tests/Component/Scaffold/Fixtures.php
index d66d7802f9239ead0482da45250157c15c2b3689..05bb3da9603c492d644d1a4406458d113bafe7d3 100644
--- a/core/tests/Drupal/Tests/Component/Scaffold/Fixtures.php
+++ b/core/tests/Drupal/Tests/Component/Scaffold/Fixtures.php
@@ -19,6 +19,13 @@
  */
 class Fixtures {
 
+  /**
+   * Keep a persistent prefix to help group our tmp directories together.
+   *
+   * @var string
+   */
+  protected static $randomPrefix = '';
+
   /**
    * Directories to delete when we are done.
    *
@@ -167,7 +174,7 @@ public function sourcePath($project_name, $source) {
    * @see \Drupal\Component\Scaffold\ManageOptions::getLocationReplacements()
    */
   public function getLocationReplacements() {
-    $destinationTmpDir = $this->mkTmpDir();
+    $destinationTmpDir = $this->mkTmpDir('location-replacements');
     $interpolator = new Interpolator();
     $interpolator->setData(['web-root' => $destinationTmpDir, 'package-name' => 'fixtures/tmp-destination']);
     return $interpolator;
@@ -237,29 +244,47 @@ public function destinationPath($destination, Interpolator $interpolator = NULL,
   /**
    * Generates a path to a temporary location, but do not create the directory.
    *
-   * @param string $extraSalt
-   *   Extra characters to throw into the md5 to add to name.
+   * @param string $prefix
+   *   A prefix for the temporary directory name.
    *
    * @return string
    *   Path to temporary directory
    */
-  public function tmpDir($extraSalt = '') {
-    $tmpDir = sys_get_temp_dir() . '/composer-scaffold-test-' . md5($extraSalt . microtime());
+  public function tmpDir($prefix) {
+    $prefix .= static::persistentPrefix();
+    $tmpDir = sys_get_temp_dir() . '/scaffold-' . $prefix . uniqid(md5($prefix . microtime()), TRUE);
     $this->tmpDirs[] = $tmpDir;
     return $tmpDir;
   }
 
+  /**
+   * Generates a persistent prefix to use with all of our temporary directories.
+   *
+   * The presumption is that this should reduce collisions in highly-parallel
+   * tests. We prepend the process id to play nicely with phpunit process
+   * isolation.
+   *
+   * @return string
+   *   A random string that will remain the same for the entire process run.
+   */
+  protected static function persistentPrefix() {
+    if (empty(static::$randomPrefix)) {
+      static::$randomPrefix = getmypid() . md5(microtime());
+    }
+    return static::$randomPrefix;
+  }
+
   /**
    * Creates a temporary directory.
    *
-   * @param string $extraSalt
-   *   Extra characters to throw into the md5 to add to name.
+   * @param string $prefix
+   *   A prefix for the temporary directory name.
    *
    * @return string
    *   Path to temporary directory
    */
-  public function mkTmpDir($extraSalt = '') {
-    $tmpDir = $this->tmpDir($extraSalt);
+  public function mkTmpDir($prefix) {
+    $tmpDir = $this->tmpDir($prefix);
     $filesystem = new Filesystem();
     $filesystem->ensureDirectoryExists($tmpDir);
     return $tmpDir;
@@ -342,28 +367,21 @@ public function runScaffold($cwd) {
    *   The Composer command to execute (escaped as required)
    * @param string $cwd
    *   The current working directory to run the command from.
-   * @param int $expectedExitCode
-   *   The expected exit code; will throw if a different exit code is returned.
    *
    * @return string
    *   Standard output and standard error from the command.
    */
-  public function runComposer($cmd, $cwd, $expectedExitCode = 0) {
+  public function runComposer($cmd, $cwd) {
     chdir($cwd);
     $input = new StringInput($cmd);
     $output = new BufferedOutput();
     $application = new Application();
     $application->setAutoExit(FALSE);
-    try {
-      $exitCode = $application->run($input, $output);
-      if ($exitCode != $expectedExitCode) {
-        print "Command '{$cmd}' - Expected exit code: {$expectedExitCode}, actual exit code: {$exitCode}\n";
-      }
-    }
-    catch (\Exception $e) {
-      print "Exception: " . $e->getMessage() . "\n";
-    }
+    $exitCode = $application->run($input, $output);
     $output = $output->fetch();
+    if ($exitCode != 0) {
+      throw new \Exception("Fixtures::runComposer failed to set up fixtures.\n\nCommand: '{$cmd}'\nExit code: {$exitCode}\nOutput: \n\n$output");
+    }
     return $output;
   }
 
diff --git a/core/tests/Drupal/Tests/Component/Scaffold/Functional/ManageGitIgnoreTest.php b/core/tests/Drupal/Tests/Component/Scaffold/Functional/ManageGitIgnoreTest.php
index c27e3e2fdfe86bdf4a9dc6e446a1d0ec8b7828e1..ddba6f1a404d88d2beada2656f3c5d514a9b3605 100644
--- a/core/tests/Drupal/Tests/Component/Scaffold/Functional/ManageGitIgnoreTest.php
+++ b/core/tests/Drupal/Tests/Component/Scaffold/Functional/ManageGitIgnoreTest.php
@@ -169,6 +169,7 @@ public function testUnmanagedGitIgnoreWhenGitNotAvailable() {
     $this->assertFileNotExists($sut . '/docroot/sites/.gitignore');
     // Confirm that 'git' is available (n.b. if it were not, createSutWithGit()
     // would fail).
+    $output = [];
     exec('git --help', $output, $status);
     $this->assertEquals(0, $status);
     // Modify our $PATH so that it begins with a path that contains an
@@ -177,12 +178,44 @@ public function testUnmanagedGitIgnoreWhenGitNotAvailable() {
     // not need to restore the PATH when we are done.
     $unavailableGitPath = $this->fixtures->binFixtureDir('disable-git-bin');
     chmod($unavailableGitPath . '/git', 0755);
+    $oldPath = getenv('PATH');
     putenv('PATH=' . $unavailableGitPath . ':' . getenv('PATH'));
     // Confirm that 'git' is no longer available.
+    $output = [];
     exec('git --help', $output, $status);
     $this->assertEquals(127, $status);
     // Run the scaffold command.
+    $output = [];
     exec('composer composer:scaffold', $output, $status);
+
+    putenv('PATH=' . $oldPath . ':' . getenv('PATH'));
+
+    $expected = <<<EOT
+0
+
+Scaffolding files for fixtures/drupal-assets-fixture:
+  - Copy [web-root]/.csslintrc from assets/.csslintrc
+  - Copy [web-root]/.editorconfig from assets/.editorconfig
+  - Copy [web-root]/.eslintignore from assets/.eslintignore
+  - Copy [web-root]/.eslintrc.json from assets/.eslintrc.json
+  - Copy [web-root]/.gitattributes from assets/.gitattributes
+  - Copy [web-root]/.ht.router.php from assets/.ht.router.php
+  - Skip [web-root]/.htaccess: overridden in fixtures/drupal-composer-drupal-project
+  - Copy [web-root]/sites/default/default.services.yml from assets/default.services.yml
+  - Skip [web-root]/sites/default/default.settings.php: overridden in fixtures/scaffold-override-fixture
+  - Copy [web-root]/sites/example.settings.local.php from assets/example.settings.local.php
+  - Copy [web-root]/sites/example.sites.php from assets/example.sites.php
+  - Copy [web-root]/index.php from assets/index.php
+  - Skip [web-root]/robots.txt: overridden in fixtures/drupal-composer-drupal-project
+  - Copy [web-root]/update.php from assets/update.php
+  - Copy [web-root]/web.config from assets/web.config
+Scaffolding files for fixtures/scaffold-override-fixture:
+  - Copy [web-root]/sites/default/default.settings.php from assets/override-settings.php
+Scaffolding files for fixtures/drupal-composer-drupal-project:
+  - Skip [web-root]/.htaccess: disabled
+  - Copy [web-root]/robots.txt from assets/robots-default.txt
+EOT;
+    $this->assertEquals($expected, $status . "\n\n" . implode("\n", $output));
     $this->assertFileExists($sut . '/docroot/index.php');
     $this->assertFileNotExists($sut . '/docroot/sites/default/.gitignore');
   }