Skip to content
Snippets Groups Projects
Commit 3f4172c4 authored by Adam G-H's avatar Adam G-H
Browse files

Issue #3239466 by phenaproxima: Refactor build tests to use core-supported project templates

parent 3e6b2027
No related branches found
No related tags found
1 merge request!57Issue #3239466: Refactor build tests to use core-supported project templates
......@@ -31,10 +31,17 @@ build:
# Add our words to the dictionary
- sed -i "s/abiword/updater's/" core/misc/cspell/dictionary.txt
- sed -i "s/absolutezero/stager's/" core/misc/cspell/dictionary.txt
- sed -i "s/adamson/STACKLIMIT/" core/misc/cspell/dictionary.txt
# After all of the shenanigans above, we're finally ready to run core's `commit-code-check.sh`! :)
- "modules/contrib/automatic_updates/commit-code-check.sh --drupalci"
# Restore the original permissions.
- chmod 777 modules/contrib/automatic_updates/
# Disable the PCRE engine's JIT, since it causes Composer to die during the
# update process, but only on Drupal CI, and for reasons that are essentially
# impossible to trace into. The PCRE JIT is not necessary for Automatic Updates
# to work correctly, and disabling it is a known workaround.
# @see pcre.ini
- sudo cp modules/contrib/automatic_updates/pcre.ini /usr/local/etc/php/conf.d
halt-on-fail: true
# run_tests task is executed several times in order of performance speeds.
# halt-on-fail can be set on the run_tests tasks in order to fail fast.
......
# This file is used on Drupal CI because Composer occasionally dies with
# PCRE_JIT_STACKLIMIT_ERROR during certain test cases, and there is no insight
# into where the failure is happening, or why. This has only been observed on
# Drupal CI. Since the PCRE engine's JIT is not strictly necessary to the
# functioning of the Automatic Updates module, this file disables the PCRE JIT
# as a workaround.
# @see drupalci.yml
pcre.jit = 0
......@@ -70,6 +70,11 @@ class ExcludedPathsSubscriber implements EventSubscriberInterface {
// Don't copy anything from the staging area's sites/default.
// @todo Make this a lot smarter in https://www.drupal.org/i/3228955.
$event->excludePath('sites/default');
// If the core-vendor-hardening plugin (used in the legacy-project template)
// is present, it may have written a web.config file into the vendor
// directory. We don't want to copy that.
$event->excludePath('web.config');
}
/**
......@@ -82,6 +87,12 @@ class ExcludedPathsSubscriber implements EventSubscriberInterface {
// Automated test site directories should never be staged.
$event->excludePath('sites/simpletest');
// Windows server configuration files, like web.config, should never be
// staged either. (These can be written in the vendor directory by the
// core-vendor-hardening plugin, which is used in the drupal/legacy-project
// template.)
$event->excludePath('web.config');
if ($public = $this->getFilesPath('public')) {
$event->excludePath($public);
}
......
<?php
namespace Drupal\Tests\automatic_updates\Build;
/**
* Tests an end-to-end core update via the core-recommended metapackage.
*
* @group automatic_updates
*/
class CoreRecommendedUpdateTest extends CoreUpdateTest {
/**
* {@inheritdoc}
*/
protected $webRoot = 'docroot/';
/**
* {@inheritdoc}
*/
protected function getConfigurationForUpdate(string $version): array {
$changes = parent::getConfigurationForUpdate($version);
// Create a fake version of drupal/core-recommended which requires the
// target version of drupal/core.
$dir = $this->copyPackage($this->getDrupalRoot() . '/composer/Metapackage/CoreRecommended');
$this->alterPackage($dir, [
'version' => $version,
'require' => [
'drupal/core' => $version,
],
]);
$changes['repositories']['drupal/core-recommended']['url'] = $dir;
return $changes;
}
/**
* {@inheritdoc}
*/
protected function getInitialConfiguration(): array {
$configuration = parent::getInitialConfiguration();
// Use drupal/core-recommended to build the test site, instead of directly
// requiring drupal/core.
$require = &$configuration['require'];
$require['drupal/core-recommended'] = $require['drupal/core'];
unset($require['drupal/core']);
$configuration['repositories']['drupal/core-recommended'] = [
'type' => 'path',
'url' => implode(DIRECTORY_SEPARATOR, [
$this->getDrupalRoot(),
'composer',
'Metapackage',
'CoreRecommended',
]),
];
return $configuration;
}
}
......@@ -12,16 +12,14 @@ class CoreUpdateTest extends UpdateTestBase {
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
protected function createTestSite(string $template): void {
// Build the test site and alter its copy of core so that it thinks it's
// running Drupal 9.8.0, which will never actually exist in the real world.
// Then, prepare a secondary copy of the core code base, masquerading as
// Drupal 9.8.1, which will be the version of core we update to. These two
// versions are referenced in the fake release metadata in our fake release
// metadata (see fixtures/release-history/drupal.0.0.xml).
$this->createTestSite();
parent::createTestSite($template);
$this->setCoreVersion($this->getWebRoot() . '/core', '9.8.0');
$this->alterPackage($this->getWorkspaceDirectory(), $this->getConfigurationForUpdate('9.8.1'));
......@@ -83,38 +81,69 @@ class CoreUpdateTest extends UpdateTestBase {
* The changes to merge into the test site's composer.json.
*/
protected function getConfigurationForUpdate(string $version): array {
$changes = [];
// Create a fake version of core with the given version number, and change
// its README so that we can actually be certain that we update to this
// fake version.
$dir = $this->copyPackage($this->getWebRoot() . '/core');
$this->setCoreVersion($dir, $version);
file_put_contents("$dir/README.txt", "Placeholder for Drupal core $version.");
$core_dir = $this->copyPackage($this->getWebRoot() . '/core');
$this->setCoreVersion($core_dir, $version);
file_put_contents("$core_dir/README.txt", "Placeholder for Drupal core $version.");
$changes['repositories']['drupal/core'] = $this->createPathRepository($core_dir);
// Create a fake version of drupal/core-recommended which itself requires
// the fake version of core we just created.
$recommended_dir = $this->copyPackage($this->getDrupalRoot() . '/composer/Metapackage/CoreRecommended');
$this->alterPackage($recommended_dir, [
'require' => [
'drupal/core' => $version,
],
'version' => $version,
]);
$changes['repositories']['drupal/core-recommended'] = $this->createPathRepository($recommended_dir);
return $changes;
}
/**
* Data provider for end-to-end update tests.
*
* @return array[]
* Sets of arguments to pass to the test method.
*/
public function providerTemplate(): array {
return [
'repositories' => [
'drupal/core' => [
'type' => 'path',
'url' => $dir,
'options' => [
'symlink' => FALSE,
],
],
],
['drupal/recommended-project'],
['drupal/legacy-project'],
];
}
/**
* Tests an end-to-end core update via the API.
*
* @param string $template
* The template project from which to build the test site.
*
* @dataProvider providerTemplate
*/
public function testApi(): void {
public function testApi(string $template): void {
$this->createTestSite($template);
$this->visit('/automatic-update-test/update/9.8.1');
$this->getMink()->assertSession()->pageTextContains('9.8.1');
}
/**
* Tests an end-to-end core update via the UI.
*
* @param string $template
* The template project from which to build the test site.
*
* @dataProvider providerTemplate
*/
public function testUi(): void {
public function testUi(string $template): void {
$this->createTestSite($template);
$mink = $this->getMink();
$page = $mink->getSession()->getPage();
$assert_session = $mink->assertSession();
......@@ -134,8 +163,15 @@ class CoreUpdateTest extends UpdateTestBase {
/**
* Tests an end-to-end core update via cron.
*
* @param string $template
* The template project from which to build the test site.
*
* @dataProvider providerTemplate
*/
public function testCron(): void {
public function testCron(string $template): void {
$this->createTestSite($template);
$this->visit('/admin/reports/status');
$this->getMink()->getSession()->getPage()->clickLink('Run cron');
$this->assertUpdateSuccessful();
......
......@@ -3,6 +3,7 @@
namespace Drupal\Tests\automatic_updates\Build;
use Drupal\BuildTests\QuickStart\QuickStartTestBase;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
use Drupal\Tests\automatic_updates\Traits\LocalPackagesTrait;
use Drupal\Tests\automatic_updates\Traits\SettingsTrait;
......@@ -29,8 +30,10 @@ abstract class UpdateTestBase extends QuickStartTestBase {
* The test site's document root, relative to the workspace directory.
*
* @var string
*
* @see ::createTestSite()
*/
protected $webRoot = './';
private $webRoot;
/**
* {@inheritdoc}
......@@ -105,17 +108,6 @@ END;
$this->addSettings($code, $this->getWebRoot());
}
/**
* Runs a Composer command and asserts that it succeeded.
*
* @param string $command
* The command to run, excluding the 'composer' prefix.
*/
protected function runComposer(string $command): void {
$this->executeCommand("composer $command");
$this->assertCommandSuccessful();
}
/**
* {@inheritdoc}
*/
......@@ -147,14 +139,52 @@ END;
/**
* Uses our already-installed dependencies to build a test site to update.
*
* @param string $template
* The template project from which to build the test site. Can be
* 'drupal/recommended-project' or 'drupal/legacy-project'.
*/
protected function createTestSite(): void {
// The project-level composer.json lives in the workspace root directory,
// which may or may not be the same directory as the web root (where Drupal
// itself lives).
$composer = $this->getWorkspaceDirectory() . DIRECTORY_SEPARATOR . 'composer.json';
$this->writeJson($composer, $this->getInitialConfiguration());
$this->runComposer('update');
protected function createTestSite(string $template): void {
// Create the test site using one of the core project templates, but don't
// install dependencies just yet.
$template_dir = implode(DIRECTORY_SEPARATOR, [
$this->getDrupalRoot(),
'composer',
'Template',
]);
$recommended_template = $this->createPathRepository($template_dir . DIRECTORY_SEPARATOR . 'RecommendedProject');
$legacy_template = $this->createPathRepository($template_dir . DIRECTORY_SEPARATOR . 'LegacyProject');
$dir = $this->getWorkspaceDirectory();
$command = sprintf(
"composer create-project %s %s --no-install --stability dev --repository '%s' --repository '%s'",
$template,
$dir,
Json::encode($recommended_template),
Json::encode($legacy_template)
);
$this->executeCommand($command);
$this->assertCommandSuccessful();
$composer = $dir . DIRECTORY_SEPARATOR . 'composer.json';
$data = $this->readJson($composer);
// Allow the test to configure the test site as necessary.
$data = $this->getInitialConfiguration($data);
// We need to know the path of the web root, relative to the project root,
// in order to install Drupal or visit the test site at all. Luckily, both
// template projects define this because the scaffold plugin needs to know
// it as well.
// @see ::visit()
// @see ::formLogin()
// @see ::installQuickStart()
$this->webRoot = $data['extra']['drupal-scaffold']['locations']['web-root'];
// Update the test site's composer.json and install dependencies.
$this->writeJson($composer, $data);
$this->executeCommand('composer install --no-dev');
$this->assertCommandSuccessful();
}
/**
......@@ -162,71 +192,50 @@ END;
*
* This configuration will be used to build the pre-update test site.
*
* @param array $data
* The current contents of the test site's composer.json.
*
* @return array
* The data that should be written to the test site's composer.json.
*/
protected function getInitialConfiguration(): array {
$core_constraint = preg_replace('/\.[0-9]+-dev$/', '.x-dev', \Drupal::VERSION);
protected function getInitialConfiguration(array $data): array {
$drupal_root = $this->getDrupalRoot();
$repositories = [
'drupal/core-composer-scaffold' => [
'type' => 'path',
'url' => implode(DIRECTORY_SEPARATOR, [
$drupal_root,
'composer',
'Plugin',
'Scaffold',
]),
],
'drupal/automatic_updates' => [
'type' => 'path',
'url' => __DIR__ . '/../../..',
],
];
$core_composer_dir = $drupal_root . DIRECTORY_SEPARATOR . 'composer';
$repositories = [];
// Add all the metapackages that are provided by Drupal core.
$metapackage_dir = $core_composer_dir . DIRECTORY_SEPARATOR . 'Metapackage';
$repositories['drupal/core-recommended'] = $this->createPathRepository($metapackage_dir . DIRECTORY_SEPARATOR . 'CoreRecommended');
$repositories['drupal/core-dev'] = $this->createPathRepository($metapackage_dir . DIRECTORY_SEPARATOR . 'DevDependencies');
// Add all the Composer plugins that are provided by Drupal core.
$plugin_dir = $core_composer_dir . DIRECTORY_SEPARATOR . 'Plugin';
$repositories['drupal/core-project-message'] = $this->createPathRepository($plugin_dir . DIRECTORY_SEPARATOR . 'ProjectMessage');
$repositories['drupal/core-composer-scaffold'] = $this->createPathRepository($plugin_dir . DIRECTORY_SEPARATOR . 'Scaffold');
$repositories['drupal/core-vendor-hardening'] = $this->createPathRepository($plugin_dir . DIRECTORY_SEPARATOR . 'VendorHardening');
$repositories = array_merge($repositories, $this->getLocalPackageRepositories($drupal_root));
// To ensure the test runs entirely offline, don't allow Composer to contact
// Packagist.
$repositories['packagist.org'] = FALSE;
return [
'require' => [
// Allow packages to be placed in their right Drupal-findable places.
'composer/installers' => '^1.9',
// Use whatever the current branch of automatic_updates is.
'drupal/automatic_updates' => '*',
// Ensure we have all files that the test site needs.
'drupal/core-composer-scaffold' => '*',
// Require the current version of core, to install its dependencies.
'drupal/core' => $core_constraint,
],
// Since Drupal 9 requires PHP 7.3 or later, these packages are probably
// not installed, which can cause trouble during dependency resolution.
// The drupal/drupal package (defined with a composer.json that is part
// of core's repository) replaces these, so we need to emulate that here.
'replace' => [
'symfony/polyfill-php72' => '*',
'symfony/polyfill-php73' => '*',
],
'repositories' => $repositories,
'extra' => [
'drupal-scaffold' => [
'locations' => [
'web-root' => $this->webRoot,
],
],
'installer-paths' => [
$this->webRoot . 'core' => [
'type:drupal-core',
],
$this->webRoot . 'modules/{$name}' => [
'type:drupal-module',
],
],
],
'minimum-stability' => 'dev',
'prefer-stable' => TRUE,
$repositories['drupal/automatic_updates'] = [
'type' => 'path',
'url' => __DIR__ . '/../../..',
];
// Use whatever the current branch of automatic_updates is.
$data['require']['drupal/automatic_updates'] = '*';
$data['repositories'] = $repositories;
// Since Drupal 9 requires PHP 7.3 or later, these packages are probably
// not installed, which can cause trouble during dependency resolution.
// The drupal/drupal package (defined with a composer.json that is part
// of core's repository) replaces these, so we need to emulate that here.
$data['replace']['symfony/polyfill-php72'] = '*';
$data['replace']['symfony/polyfill-php73'] = '*';
return $data;
}
/**
......
......@@ -107,17 +107,30 @@ trait LocalPackagesTrait {
$this->writeJson($composer, $package);
$name = $package['name'];
$repositories[$name] = [
'type' => 'path',
'url' => $path,
'options' => [
'symlink' => FALSE,
],
];
$repositories[$name] = $this->createPathRepository($path);
}
return $repositories;
}
/**
* Defines a local path repository for a given path.
*
* @param string $path
* The path of the repository.
*
* @return array
* The local path repository definition.
*/
protected function createPathRepository(string $path): array {
return [
'type' => 'path',
'url' => $path,
'options' => [
'symlink' => FALSE,
],
];
}
/**
* Alters a package's composer.json file.
*
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment