From 4321021e2adefa08660ce42a3b0bcb1c12effec0 Mon Sep 17 00:00:00 2001 From: drugan Date: Wed, 1 Mar 2017 17:01:26 +0200 Subject: [PATCH] Issue #2745123 by drugan, mondrake, Mile23, benjifisher, penyaskito, hgoto, othermachines, Jaypan, alexpott: Simpletest module crashes on cleanup, uninstall --- core/modules/simpletest/simpletest.module | 61 +++++++++++++++-- .../src/Tests/UiCleanTemporaryDirectoriesTest.php | 70 ++++++++++++++++++++ .../testing_page_test/testing_page_test.info.yml | 6 ++ .../testing_page_test/testing_page_test.module | 22 ++++++ 4 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 core/modules/simpletest/src/Tests/UiCleanTemporaryDirectoriesTest.php create mode 100644 core/modules/simpletest/tests/modules/testing_page_test/testing_page_test.info.yml create mode 100644 core/modules/simpletest/tests/modules/testing_page_test/testing_page_test.module diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index c9eed3b..ba07282 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -684,16 +684,61 @@ 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. + * + * @return null|bool + * Returns TRUE if all attempted to remove directories are removed, FALSE if + * at least one directory was not removed successfully, NULL if there is + * nothing to remove or the directory is not eligible for removing. + * + * @see file_unmanaged_delete() + * @see http://php.net/manual/en/function.unlink.php */ -function simpletest_clean_temporary_directories() { +function simpletest_clean_temporary_directories($directory = NULL) { + $directories = []; $count = 0; - if (is_dir(DRUPAL_ROOT . '/sites/simpletest')) { - $files = scandir(DRUPAL_ROOT . '/sites/simpletest'); - foreach ($files as $file) { + $result = NULL; + $simpletest_root = \Drupal::root() . '/sites/simpletest/'; + + if (is_dir($simpletest_root)) { + // If the $directory is not valid string or NULL then get the type of it for + // debugging purposes in the watchdog error message below. + $path = is_string($directory) && !empty($directory) ? $directory : gettype($directory); + // Do not recognize any, except expected directory pattern as wrong + // directory being passed accidentally may cause catastrophic consequences. + preg_match('/^(sites\/simpletest\/)(.*)/', $path, $matches); + + if (!empty($matches[2]) && is_dir($simpletest_root . $matches[2])) { + $directories[] = $matches[2]; + } + elseif ($directory === NULL) { + $directories = scandir($simpletest_root); + } + else { + drupal_set_message(t('The %path is not eligible for removing in %func().', ['%path' => $path, '%func' => __FUNCTION__])); + } + + foreach ($directories as $file) { if ($file[0] != '.') { - $path = DRUPAL_ROOT . '/sites/simpletest/' . $file; - file_unmanaged_delete_recursive($path, array('Drupal\simpletest\TestBase', 'filePreDeleteCallback')); - $count++; + $path = $simpletest_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. + $deleted = file_unmanaged_delete_recursive($path, function ($any_path) { + @chmod($any_path, 0700); + }); + $result = $result === FALSE ? $result : $deleted; + if ($deleted) { + $count++; + } + else { + drupal_set_message(t('This directory is failed to be removed: @path.', ['@path' => $path])); + } } } } @@ -704,6 +749,8 @@ function simpletest_clean_temporary_directories() { else { drupal_set_message(t('No temporary directories to remove.')); } + + return $result; } /** diff --git a/core/modules/simpletest/src/Tests/UiCleanTemporaryDirectoriesTest.php b/core/modules/simpletest/src/Tests/UiCleanTemporaryDirectoriesTest.php new file mode 100644 index 0000000..05235f4 --- /dev/null +++ b/core/modules/simpletest/src/Tests/UiCleanTemporaryDirectoriesTest.php @@ -0,0 +1,70 @@ +drupalLogin($this->drupalCreateUser(array('administer unit tests'))); + } + + /** + * Tests removing a temporary directory through the UI. + */ + public function testCleanTemporaryDirectories() { + $simpletest = \Drupal::root() . '/sites/simpletest'; + $test_site_directory = basename($this->siteDirectory); + $clean_environment = t('Clean environment'); + $this->drupalGet('admin/config/development/testing'); + + $this->assertFieldByXPath('//input', $clean_environment, 'Displayed the "Clean environment" button.'); + $this->assertNull(simpletest_clean_temporary_directories('sites/simpletest/test_site_directory'), 'The sites/simpletest/test_site_directory folder does not exist.'); + + $this->recurse_copy_test_site_directory("$simpletest/$test_site_directory", "$simpletest/test_site_directory"); + + $this->assertTrue(is_dir("$simpletest/test_site_directory"), 'The test site directory is copied to the sites/simpletest/test_site_directory folder.'); + + $this->drupalPostForm(NULL, [], $clean_environment); + + $this->assertText(t('Removed @count temporary directory.', ['@count' => 1]), 'Displayed a message: "Removed 1 temporary directory.".'); + $this->assertNoText(t('This directory is failed to be removed:'), 'The message "This directory is failed to be removed:" is not displayed.'); + $this->assertNull(simpletest_clean_temporary_directories('sites/simpletest/test_site_directory'), 'The sites/simpletest/test_site_directory folder does not exist any more.'); + } + + /** + * Creates the exact copy of the test site temporary directory. + */ + protected function recurse_copy_test_site_directory($source, $destination) { + $directory = opendir($source); + mkdir($destination); + + while(($file = readdir($directory)) !== FALSE) { + if ($file != '.' && $file != '..') { + if (is_dir("$source/$file")) { + $this->recurse_copy_test_site_directory("$source/$file", "$destination/$file"); + } + else { + copy("$source/$file", "$destination/$file"); + } + } + } + closedir($directory); + } + +} diff --git a/core/modules/simpletest/tests/modules/testing_page_test/testing_page_test.info.yml b/core/modules/simpletest/tests/modules/testing_page_test/testing_page_test.info.yml new file mode 100644 index 0000000..33773ba --- /dev/null +++ b/core/modules/simpletest/tests/modules/testing_page_test/testing_page_test.info.yml @@ -0,0 +1,6 @@ +name: 'Testing page test' +type: module +description: 'Provides customizations for the simpletest testing page.' +package: Testing +version: VERSION +core: 8.x diff --git a/core/modules/simpletest/tests/modules/testing_page_test/testing_page_test.module b/core/modules/simpletest/tests/modules/testing_page_test/testing_page_test.module new file mode 100644 index 0000000..da2d82b --- /dev/null +++ b/core/modules/simpletest/tests/modules/testing_page_test/testing_page_test.module @@ -0,0 +1,22 @@ +