Issue #3265325 by xjm, Wim Leers, daffie: Raise a warning instead of an error when installing on MINIMUM_SUPPORTED_PHP

(cherry picked from commit e8b2bec983)
merge-requests/1575/head
Alex Pott 2022-03-15 10:50:04 +00:00
parent e5590b2254
commit 3e6b51d96d
No known key found for this signature in database
GPG Key ID: BDA67E7EE836E5CE
11 changed files with 191 additions and 71 deletions

View File

@ -5,6 +5,7 @@ namespace Drupal\Tests\config\Functional;
use Drupal\FunctionalTests\Installer\InstallerTestBase;
use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Serialization\Yaml;
use Drupal\Tests\RequirementsPageTrait;
/**
* Tests install profile config overrides can not add unmet dependencies.
@ -13,6 +14,8 @@ use Drupal\Core\Serialization\Yaml;
*/
class ConfigInstallProfileUnmetDependenciesTest extends InstallerTestBase {
use RequirementsPageTrait;
/**
* The installation profile to install.
*

View File

@ -294,27 +294,17 @@ function system_requirements($phase) {
':php_requirements' => 'https://www.drupal.org/docs/9/how-drupal-9-is-made-and-what-is-included/environment-requirements-of-drupal-9#s-php-version-requirement',
]
);
$requirements['php']['severity'] = REQUIREMENT_ERROR;
// If the PHP version is also below the absolute minimum allowed, it's not
// safe to continue with the requirements check.
// safe to continue with the requirements check, and should always be an
// error.
if (version_compare($phpversion, \Drupal::MINIMUM_PHP) < 0) {
$requirements['php']['severity'] = REQUIREMENT_ERROR;
return $requirements;
}
// Otherwise downgrade the error to a warning during updates. Even if there
// are some problems with the site's PHP version, it's still better for the
// site to keep its Drupal codebase up to date.
elseif ($phase === 'update') {
$requirements['php']['severity'] = REQUIREMENT_WARNING;
}
// Since we allow sites with unsupported PHP versions to still run Drupal
// updates, we also need to be able to run tests with those PHP versions,
// which requires the ability to install test sites. Not all tests are
// required to pass on these PHP versions, but we want to monitor which
// ones do and don't.
elseif ($phase === 'install' && drupal_valid_test_ua()) {
$requirements['php']['severity'] = REQUIREMENT_INFO;
}
// Otherwise, the message should be an error at runtime, and a warning
// during installation or update.
$requirements['php']['severity'] = ($phase === 'runtime') ? REQUIREMENT_ERROR : REQUIREMENT_WARNING;
}
// For PHP versions that are still supported but no longer recommended,
// inform users of what's recommended, allowing them to take action before it

View File

@ -0,0 +1,81 @@
<?php
namespace Drupal\Tests\system\Functional\System;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\RequirementsPageTrait;
/**
* Tests the output of PHP requirements on the status report.
*
* @group system
*/
class PhpRequirementTest extends BrowserTestBase {
use RequirementsPageTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$admin_user = $this->drupalCreateUser([
'administer site configuration',
'access site reports',
]);
$this->drupalLogin($admin_user);
// By default, Drupal installation (and BrowserTestBase) do not configure
// trusted host patterns, which leads to an error on the status report.
// Configure them so that the site is properly configured and so that we
// can cleanly test the errors related to PHP versions.
$settings['settings']['trusted_host_patterns'] = (object) [
'value' => ['^' . preg_quote(\Drupal::request()->getHost()) . '$'],
'required' => TRUE,
];
$this->writeSettings($settings);
}
/**
* Tests status report messages regarding the PHP version.
*/
public function testStatusPage() {
// Go to Administration.
$this->drupalGet('admin/reports/status');
$this->assertSession()->statusCodeEquals(200);
$phpversion = phpversion();
// Verify that the PHP version is shown on the page.
$this->assertSession()->pageTextContains($phpversion);
// Verify that an error is displayed about the PHP version if it is below
// the minimum supported PHP.
if (version_compare($phpversion, \Drupal::MINIMUM_SUPPORTED_PHP) < 0) {
$this->assertErrorSummaries(['PHP']);
$this->assertSession()->pageTextContains('Your PHP installation is too old. Drupal requires at least PHP ' . \Drupal::MINIMUM_SUPPORTED_PHP);
}
// Otherwise, there should be no error.
else {
$this->assertSession()->pageTextNotContains('Your PHP installation is too old. Drupal requires at least PHP ' . \Drupal::MINIMUM_SUPPORTED_PHP);
$this->assertSession()->pageTextNotContains('Errors found');
}
// There should be an informational message if the PHP version is below the
// recommended version.
if (version_compare($phpversion, \Drupal::RECOMMENDED_PHP) < 0) {
$this->assertSession()->pageTextContains('It is recommended to upgrade to PHP version ' . \Drupal::RECOMMENDED_PHP . ' or higher');
}
// Otherwise, the message should not be there.
else {
$this->assertSession()->pageTextNotContains('It is recommended to upgrade to PHP version ' . \Drupal::RECOMMENDED_PHP . ' or higher');
}
}
}

View File

@ -47,6 +47,15 @@ class InstallerConfigDirectorySetNoDirectoryErrorTest extends InstallerTestBase
// screen.
}
/**
* {@inheritdoc}
*/
protected function setUpRequirementsProblem() {
// The parent method asserts that there are no requirements errors, but
// this test expects a requirements error in the test method below.
// Therefore, we override this method to suppress the parent's assertions.
}
/**
* {@inheritdoc}
*/

View File

@ -50,6 +50,15 @@ class InstallerExistingBrokenDatabaseSettingsTest extends InstallerTestBase {
// This form will never be reached.
}
/**
* {@inheritdoc}
*/
protected function setUpRequirementsProblem() {
// The parent method asserts that there are no requirements errors, but
// this test expects a requirements error in the test method below.
// Therefore, we override this method to suppress the parent's assertions.
}
/**
* {@inheritdoc}
*/

View File

@ -34,6 +34,15 @@ EOF;
// There are errors therefore there is nothing to do here.
}
/**
* {@inheritdoc}
*/
protected function setUpRequirementsProblem() {
// The parent method asserts that there are no requirements errors, but
// this test expects a requirements error in the test method below.
// Therefore, we override this method to suppress the parent's assertions.
}
/**
* Final installer step: Configure site.
*/

View File

@ -26,6 +26,15 @@ class InstallerProfileRequirementsTest extends InstallerTestBase {
// This form will never be reached.
}
/**
* {@inheritdoc}
*/
protected function setUpRequirementsProblem() {
// The parent method asserts that there are no requirements errors, but
// this test expects a requirements error in the test method below.
// Therefore, we override this method to suppress the parent's assertions.
}
/**
* {@inheritdoc}
*/

View File

@ -252,7 +252,9 @@ abstract class InstallerTestBase extends BrowserTestBase {
* @see system_requirements()
*/
protected function setUpRequirementsProblem() {
// Do nothing.
if (version_compare(phpversion(), \Drupal::MINIMUM_SUPPORTED_PHP) < 0) {
$this->continueOnExpectedWarnings(['PHP']);
}
}
/**

View File

@ -47,18 +47,32 @@ ENDPO;
* {@inheritdoc}
*/
protected function setUpProfile() {
// Do nothing, because this test only tests the language installation
// step's results.
}
/**
* {@inheritdoc}
*/
protected function setUpSettings() {
// Do nothing, because this test only tests the language installation
// step's results.
}
/**
* {@inheritdoc}
*/
protected function setUpRequirementsProblem() {
// Do nothing, because this test only tests the language installation
// step's results.
}
/**
* {@inheritdoc}
*/
protected function setUpSite() {
// Do nothing, because this test only tests the language installation
// step's results.
}
/**

View File

@ -85,9 +85,6 @@ class QuickStartTest extends TestCase {
* Tests the quick-start command.
*/
public function testQuickStartCommand() {
if (version_compare(phpversion(), \Drupal::MINIMUM_SUPPORTED_PHP) < 0) {
$this->markTestSkipped();
}
if (version_compare(\SQLite3::version()['versionString'], Tasks::SQLITE_MINIMUM_VERSION) < 0) {
$this->markTestSkipped();
}
@ -139,41 +136,10 @@ class QuickStartTest extends TestCase {
$process->stop();
}
/**
* Tests that the installer throws a requirement error on older PHP versions.
*/
public function testPhpRequirement() {
if (version_compare(phpversion(), \Drupal::MINIMUM_SUPPORTED_PHP) >= 0) {
$this->markTestSkipped();
}
$install_command = [
$this->php,
'core/scripts/drupal',
'quick-start',
'standard',
"--site-name='Test site {$this->testDb->getDatabasePrefix()}'",
'--suppress-login',
];
$process = new Process($install_command, NULL, ['DRUPAL_DEV_SITE_PATH' => $this->testDb->getTestSitePath()]);
$process->setTimeout(500);
$process->run();
$error_output = $process->getErrorOutput();
$this->assertStringContainsString('Your PHP installation is too old.', $error_output);
$this->assertStringContainsString('Drupal requires at least PHP', $error_output);
$this->assertStringContainsString(\Drupal::MINIMUM_SUPPORTED_PHP, $error_output);
// Stop the web server.
$process->stop();
}
/**
* Tests the quick-start commands.
*/
public function testQuickStartInstallAndServerCommands() {
if (version_compare(phpversion(), \Drupal::MINIMUM_SUPPORTED_PHP) < 0) {
$this->markTestSkipped();
}
if (version_compare(\SQLite3::version()['versionString'], Tasks::SQLITE_MINIMUM_VERSION) < 0) {
$this->markTestSkipped();
}

View File

@ -24,10 +24,6 @@ trait RequirementsPageTrait {
/**
* Continues installation when the expected warnings are found.
*
* This function is no longer called by any core test, but it is retained for
* use by contrib/custom tests. It is not deprecated, because it remains the
* recommended function to call for its purpose.
*
* @param string[] $expected_warnings
* A list of warning summaries to expect on the requirements screen (e.g.
* 'PHP', 'PHP OPcode caching', etc.). If only the expected warnings
@ -43,38 +39,70 @@ trait RequirementsPageTrait {
}
/**
* Assert the given warning summaries are present on the page.
* Asserts the given warning summaries are present on the page.
*
* If an expected warning is not found, or if a warning not in the list is
* present, a fail is raised.
*
* @param string[] $warning_summaries
* @param string[] $summaries
* A list of warning summaries to expect on the requirements screen (e.g.
* 'PHP', 'PHP OPcode caching', etc.).
*/
protected function assertWarningSummaries(array $warning_summaries) {
// Allow only details elements that are directly after the warning header
// or each other. There is no guaranteed wrapper we can rely on across
// distributions. When there are multiple warnings, the selectors will be:
protected function assertWarningSummaries(array $summaries) {
$this->assertRequirementSummaries($summaries, 'warning');
}
/**
* Asserts the given error summaries are present on the page.
*
* If an expected error is not found, or if an error not in the list is
* present, a fail is raised.
*
* @param string[] $summaries
* A list of error summaries to expect on the requirements screen (e.g.
* 'PHP', 'PHP OPcode caching', etc.).
*/
protected function assertErrorSummaries(array $summaries) {
$this->assertRequirementSummaries($summaries, 'error');
}
/**
* Asserts the given requirements section summaries are present on the page.
*
* If an expected requirements message is not found, or if a message not in
* the list is present, a fail is raised.
*
* @param string[] $summaries
* A list of warning summaries to expect on the requirements screen (e.g.
* 'PHP', 'PHP OPcode caching', etc.).
* @param string $type
* The type of requirement, either 'warning' or 'error'.
*/
protected function assertRequirementSummaries(array $summaries, string $type) {
// Allow only details elements that are directly after the warning/error
// header or each other. There is no guaranteed wrapper we can rely on
// across distributions. When there are multiple warnings, the selectors
// will be:
// - h3#warning+details summary
// - h3#warning+details+details summary
// - etc.
// We add one more selector than expected warnings to confirm that there
// isn't any other warning before clicking the link.
// For errors, the selectors are the same except that they are h3#error.
// We add one more selector than expected requirements to confirm that
// there isn't any other requirement message before clicking the link.
// @todo Make this more reliable in
// https://www.drupal.org/project/drupal/issues/2927345.
$selectors = [];
for ($i = 0; $i <= count($warning_summaries); $i++) {
$selectors[] = 'h3#warning' . implode('', array_fill(0, $i + 1, '+details')) . ' summary';
for ($i = 0; $i <= count($summaries); $i++) {
$selectors[] = 'h3#' . $type . implode('', array_fill(0, $i + 1, '+details')) . ' summary';
}
$warning_elements = $this->cssSelect(implode(', ', $selectors));
$elements = $this->cssSelect(implode(', ', $selectors));
// Confirm that there are only the expected warnings.
$warnings = [];
foreach ($warning_elements as $warning) {
$warnings[] = trim($warning->getText());
// Confirm that there are only the expected requirements.
$requirements = [];
foreach ($elements as $requirement) {
$requirements[] = trim($requirement->getText());
}
$this->assertEquals($warning_summaries, $warnings);
$this->assertEquals($summaries, $requirements);
}
}