From cce7f34f0546046dc9b264ebcc0fa033bb99de8d Mon Sep 17 00:00:00 2001
From: drugan <drugan@1578644.no-reply.drupal.org>
Date: Tue, 21 Feb 2017 12:24:08 +0200
Subject: [PATCH] Issue #2745123 by drugan, mondrake, Mile23, benjifisher,
 penyaskito, hgoto, othermachines, Jaypan: Simpletest module
 crashes on cleanup, uninstall

---
 core/modules/simpletest/simpletest.module   |   38 +++++++++++++++++++++++----
 core/modules/simpletest/src/TestBase.php    |   19 ++------------
 core/scripts/run-tests.sh                   |    8 +++---
 core/tests/Drupal/Tests/BrowserTestBase.php |   22 ++--------------
 4 files changed, 40 insertions(+), 47 deletions(-)

diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index c9eed3b..79689c0 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -684,15 +684,43 @@ function simpletest_clean_database() {
 
 /**
  * Finds all leftover temporary directories and removes them.
+ *
+ * @param string $directory
+ *   (optional) The relative path to directory of a particular test site. Must
+ *   to be of the following pattern: sites/simpletest/12345678. If ommited, all
+ *   the test sites' directories found in sites/simpletest will be removed.
  */
-function simpletest_clean_temporary_directories() {
+function simpletest_clean_temporary_directories($directory = NULL) {
   $count = 0;
-  if (is_dir(DRUPAL_ROOT . '/sites/simpletest')) {
-    $files = scandir(DRUPAL_ROOT . '/sites/simpletest');
+  $root = \Drupal::root() . '/sites/simpletest/';
+
+  if (is_dir($root)) {
+    // Prevent removing wrong directory accidentally passed as an argument.
+    $directory = $directory ? explode('sites/simpletest/', $directory) : FALSE;
+    $files = [];
+
+    if (isset($directory[1]) && is_dir($root . $directory[1])) {
+      $files[] = $directory[1];
+    }
+    else {
+      $files = scandir($root);
+    }
+
     foreach ($files as $file) {
       if ($file[0] != '.') {
-        $path = DRUPAL_ROOT . '/sites/simpletest/' . $file;
-        file_unmanaged_delete_recursive($path, array('Drupal\simpletest\TestBase', 'filePreDeleteCallback'));
+        $path = $root . $file;
+        // When the webserver runs with the same system user as the test
+        // runner, we can make read-only files writable again. If not, chmod
+        // will fail while the file deletion still works if file permissions
+        // have been configured correctly. Thus, we ignore any chmod errors.
+        $result = file_unmanaged_delete_recursive($path, function ($any_path) {
+          @chmod($any_path, 0700);
+        });
+        if ($directory) {
+          // We do not need any more if this function was called from within a
+          // running test instead of using "Clean environment" button.
+          return $result;
+        }
         $count++;
       }
     }
diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php
index 14d7909..b6e2c3e 100644
--- a/core/modules/simpletest/src/TestBase.php
+++ b/core/modules/simpletest/src/TestBase.php
@@ -1231,8 +1231,8 @@ private function restoreEnvironment() {
     $this->container = $this->originalContainer;
     \Drupal::setContainer($this->originalContainer);
 
-    // Delete test site directory.
-    file_unmanaged_delete_recursive($this->siteDirectory, array($this, 'filePreDeleteCallback'));
+    // Delete the current test site directory.
+    simpletest_clean_temporary_directories($this->siteDirectory);
 
     // Restore original database connection.
     Database::removeConnection('default');
@@ -1393,21 +1393,6 @@ public static function generatePermutations($parameters) {
   }
 
   /**
-   * Ensures test files are deletable within file_unmanaged_delete_recursive().
-   *
-   * Some tests chmod generated files to be read only. During
-   * TestBase::restoreEnvironment() and other cleanup operations, these files
-   * need to get deleted too.
-   */
-  public static function filePreDeleteCallback($path) {
-    // When the webserver runs with the same system user as the test runner, we
-    // can make read-only files writable again. If not, chmod will fail while
-    // the file deletion still works if file permissions have been configured
-    // correctly. Thus, we ignore any problems while running chmod.
-    @chmod($path, 0700);
-  }
-
-  /**
    * Configuration accessor for tests. Returns non-overridden configuration.
    *
    * @param $name
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index 9e832a5..b1ae138 100755
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -911,11 +911,9 @@ function simpletest_script_cleanup($test_id, $test_class, $exitcode) {
         $messages[] = $errors;
       }
     }
-    // Delete the test site directory.
-    // simpletest_clean_temporary_directories() cannot be used here, since it
-    // would also delete file directories of other tests that are potentially
-    // running concurrently.
-    file_unmanaged_delete_recursive($test_directory, array('Drupal\simpletest\TestBase', 'filePreDeleteCallback'));
+    // Delete the current test site directory leaving alone other tests that
+    // are potentially running concurrently.
+    simpletest_clean_temporary_directories($test_db->getTestSitePath());
     $messages[] = "- Removed test site directory.";
   }
 
diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php
index d4968f4..554d8f1 100644
--- a/core/tests/Drupal/Tests/BrowserTestBase.php
+++ b/core/tests/Drupal/Tests/BrowserTestBase.php
@@ -457,24 +457,6 @@ protected function setUp() {
   }
 
   /**
-   * Ensures test files are deletable within file_unmanaged_delete_recursive().
-   *
-   * Some tests chmod generated files to be read only. During
-   * BrowserTestBase::cleanupEnvironment() and other cleanup operations,
-   * these files need to get deleted too.
-   *
-   * @param string $path
-   *   The file path.
-   */
-  public static function filePreDeleteCallback($path) {
-    // When the webserver runs with the same system user as phpunit, we can
-    // make read-only files writable again. If not, chmod will fail while the
-    // file deletion still works if file permissions have been configured
-    // correctly. Thus, we ignore any problems while running chmod.
-    @chmod($path, 0700);
-  }
-
-  /**
    * Clean up the Simpletest environment.
    */
   protected function cleanupEnvironment() {
@@ -492,8 +474,8 @@ protected function cleanupEnvironment() {
       }
     }
 
-    // Delete test site directory.
-    file_unmanaged_delete_recursive($this->siteDirectory, array($this, 'filePreDeleteCallback'));
+    // Delete the current test site directory.
+    simpletest_clean_temporary_directories($this->siteDirectory);
   }
 
   /**
-- 
1.7.9.5

