diff --git a/package_manager/tests/src/Build/TemplateProjectTestBase.php b/package_manager/tests/src/Build/TemplateProjectTestBase.php index 188692753dc216a64a330b1b7019535c337e3c46..47ecdac12c02bd9ba9be3faba583571de97f1c60 100644 --- a/package_manager/tests/src/Build/TemplateProjectTestBase.php +++ b/package_manager/tests/src/Build/TemplateProjectTestBase.php @@ -14,6 +14,7 @@ use Drupal\package_manager\Event\PreApplyEvent; use Drupal\package_manager\Event\PreCreateEvent; use Drupal\package_manager\Event\PreDestroyEvent; use Drupal\package_manager\Event\PreRequireEvent; +use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait; use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait; use Drupal\Tests\RandomGeneratorTrait; @@ -29,6 +30,7 @@ use Drupal\Tests\RandomGeneratorTrait; */ abstract class TemplateProjectTestBase extends QuickStartTestBase { + use AssertPreconditionsTrait; use FixtureUtilityTrait; use RandomGeneratorTrait; diff --git a/package_manager/tests/src/Functional/FailureMarkerRequirementTest.php b/package_manager/tests/src/Functional/FailureMarkerRequirementTest.php index 1f5f59c3a2e10c244610f7f7f8181e5cd8f71363..16b1ad29a7a0657cce26669f77245f93adf9c7b8 100644 --- a/package_manager/tests/src/Functional/FailureMarkerRequirementTest.php +++ b/package_manager/tests/src/Functional/FailureMarkerRequirementTest.php @@ -7,6 +7,7 @@ namespace Drupal\Tests\package_manager\Functional; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\package_manager\Stage; use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait; /** * Tests that Package Manager's requirements check for the failure marker. @@ -17,6 +18,8 @@ use Drupal\Tests\BrowserTestBase; class FailureMarkerRequirementTest extends BrowserTestBase { use StringTranslationTrait; + use AssertPreconditionsTrait; + /** * {@inheritdoc} */ diff --git a/package_manager/tests/src/Kernel/ComposerUtilityTest.php b/package_manager/tests/src/Kernel/ComposerUtilityTest.php index 98906327540e25fa558c89c411b3081d3145325b..2a9187f73e249b96e11b40eb918f6f5490add76f 100644 --- a/package_manager/tests/src/Kernel/ComposerUtilityTest.php +++ b/package_manager/tests/src/Kernel/ComposerUtilityTest.php @@ -7,6 +7,7 @@ namespace Drupal\Tests\package_manager\Kernel; use Drupal\fixture_manipulator\FixtureManipulator; use Drupal\KernelTests\KernelTestBase; use Drupal\package_manager\ComposerUtility; +use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait; use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait; use org\bovigo\vfs\vfsStream; @@ -17,6 +18,7 @@ use org\bovigo\vfs\vfsStream; */ class ComposerUtilityTest extends KernelTestBase { + use AssertPreconditionsTrait; use FixtureUtilityTrait; /** diff --git a/package_manager/tests/src/Kernel/CorePackageManifestTest.php b/package_manager/tests/src/Kernel/CorePackageManifestTest.php index e5ededf42a3a90c5873ee4e6a00949a85f30fb6d..12596e0d262a43498e5769b77c0f55f0f116fea5 100644 --- a/package_manager/tests/src/Kernel/CorePackageManifestTest.php +++ b/package_manager/tests/src/Kernel/CorePackageManifestTest.php @@ -7,6 +7,7 @@ namespace Drupal\Tests\package_manager\Kernel; use Drupal\Component\Serialization\Json; use Drupal\Component\Serialization\Yaml; use Drupal\KernelTests\KernelTestBase; +use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait; use Symfony\Component\Finder\Finder; /** @@ -26,6 +27,8 @@ use Symfony\Component\Finder\Finder; */ class CorePackageManifestTest extends KernelTestBase { + use AssertPreconditionsTrait; + /** * Tests that detected core packages match our hard-coded manifest file. */ diff --git a/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php b/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php index 14e9e7da9eab102f6038bf2f07efb8ac453b15cb..c0b2f335d62c2b81dec98ad36c5b4adfca7d98aa 100644 --- a/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php +++ b/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php @@ -5,6 +5,7 @@ declare(strict_types = 1); namespace Drupal\Tests\package_manager\Kernel; use Drupal\KernelTests\KernelTestBase; +use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait; use PhpTuf\ComposerStager\Domain\Service\FileSyncer\FileSyncerInterface; use PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\PhpFileSyncer; use PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\RsyncFileSyncer; @@ -16,6 +17,8 @@ use PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\RsyncFileSyncer; */ class FileSyncerFactoryTest extends KernelTestBase { + use AssertPreconditionsTrait; + /** * {@inheritdoc} */ diff --git a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php index 266015ec8f6ee39e35319e965abfc3acf7e68b14..a54d40d60da29640a95d32987b9daf0c4e82b466 100644 --- a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php +++ b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php @@ -14,6 +14,7 @@ use Drupal\package_manager\Exception\StageException; use Drupal\package_manager\Exception\StageValidationException; use Drupal\package_manager\Stage; use Drupal\package_manager_bypass\Beginner; +use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait; use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait; use Drupal\Tests\package_manager\Traits\ValidationTestTrait; use GuzzleHttp\Client; @@ -35,6 +36,7 @@ use Symfony\Component\DependencyInjection\Definition; */ abstract class PackageManagerKernelTestBase extends KernelTestBase { + use AssertPreconditionsTrait; use FixtureUtilityTrait; use StatusCheckTrait; use ValidationTestTrait; diff --git a/package_manager/tests/src/Kernel/ServicesTest.php b/package_manager/tests/src/Kernel/ServicesTest.php index 33e3cc72874b3fe9ee2d38fdd46b3194b5929eef..e9d81754f08729faaebabd1b2b5088050e42cf46 100644 --- a/package_manager/tests/src/Kernel/ServicesTest.php +++ b/package_manager/tests/src/Kernel/ServicesTest.php @@ -7,6 +7,7 @@ namespace Drupal\Tests\package_manager\Kernel; use Drupal\KernelTests\KernelTestBase; use Drupal\package_manager\ExecutableFinder; use Drupal\package_manager\ProcessFactory; +use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait; use PhpTuf\ComposerStager\Infrastructure\Factory\Process\ProcessFactoryInterface; use PhpTuf\ComposerStager\Infrastructure\Service\Finder\ExecutableFinderInterface; @@ -18,6 +19,8 @@ use PhpTuf\ComposerStager\Infrastructure\Service\Finder\ExecutableFinderInterfac */ class ServicesTest extends KernelTestBase { + use AssertPreconditionsTrait; + /** * {@inheritdoc} */ diff --git a/package_manager/tests/src/Traits/AssertPreconditionsTrait.php b/package_manager/tests/src/Traits/AssertPreconditionsTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..1f6bfb3d4befa4a04409d2481cf420133c397db1 --- /dev/null +++ b/package_manager/tests/src/Traits/AssertPreconditionsTrait.php @@ -0,0 +1,109 @@ +<?php + +declare(strict_types = 1); + +namespace Drupal\Tests\package_manager\Traits; + +use Composer\Autoload\ClassLoader; + +/** + * Asserts preconditions for tests to function properly. + */ +trait AssertPreconditionsTrait { + + /** + * Invokes the test preconditions assertion before the first test is run. + * + * "Use" this trait on any Automatic Updates class that directly extends a + * Core test class, i.e., any class that does NOT extend a test class in a + * Automatic Updates test namespace. If that class implements this hook, too, + * be sure to call this first thing in it. + */ + public static function setUpBeforeClass(): void { + parent::setUpBeforeClass(); + static::failIfUnmetPreConditions('before'); + } + + /** + * Invokes the test preconditions assertion after each test run. + * + * This ensures that no test method leaves behind violations of test + * preconditions. This makes it trivial to discover broken tests. + */ + protected function tearDown(): void { + parent::tearDown(); + static::failIfUnmetPreConditions('after'); + } + + /** + * Asserts universal test preconditions before any setup is done. + * + * If these preconditions aren't met, automated tests will absolutely fail + * needlessly with misleading errors. In that case, there's no reason to even + * begin. + * + * Ordinarily, these preconditions would be asserted in + * ::assertPreConditions(), which PHPUnit provides for exactly this use case. + * Unfortunately, that method doesn't run until after ::setUp(), so our (many) + * tests with expensive, time-consuming setup routines wouldn't actually fail + * very early. + * + * @param string $when + * Either 'before' (before any test methods run) or 'after' (after any test + * method finishes). + * + * @see \PHPUnit\Framework\TestCase::assertPreConditions() + * @see \PHPUnit\Framework\TestCase::setUpBeforeClass() + * @see self::setupBeforeClass() + * @see self::tearDown() + */ + protected static function failIfUnmetPreConditions(string $when): void { + assert(in_array($when, ['before', 'after'], TRUE)); + static::assertNoFailureMarker($when); + } + + /** + * Asserts that there is no failure marker present. + * + * @param string $when + * Either 'before' (before any test methods run) or 'after' (after any test + * method finishes). + * + * @see \Drupal\package_manager\FailureMarker + */ + private static function assertNoFailureMarker(string $when): void { + // If the failure marker exists, it will be in the project root. The project + // root is defined as the directory containing the `vendor` directory. + // @see \Drupal\package_manager\FailureMarker::getPath() + $failure_marker = static::getProjectRoot() . '/PACKAGE_MANAGER_FAILURE.json'; + if (file_exists($failure_marker)) { + $suffix = $when === 'before' + ? 'Remove it to continue.' + : 'This test method created this marker but failed to clean up after itself.'; + static::fail("The failure marker '$failure_marker' is present in the project. $suffix"); + } + } + + /** + * Returns the absolute path of the project root. + * + * @return string + * The absolute path of the project root. + * + * @see \Drupal\package_manager\PathLocator::getProjectRoot() + */ + private static function getProjectRoot(): string { + // This is tricky, because this method has to be static (since + // ::setUpBeforeClass is), so it can't just get the container from an + // instance member. + // Use reflection to extract the vendor directory from the class loader. + $class_loader = $GLOBALS['loader']; + assert($class_loader instanceof ClassLoader); + $object = new \ReflectionObject($class_loader); + $property = $object->getProperty('vendorDir'); + $property->setAccessible(TRUE); + $vendor_directory = $property->getValue($class_loader); + return dirname($vendor_directory); + } + +} diff --git a/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php b/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php index 608b5311c4a491c506826222bce897e95d57cfdd..14761deb05ecb287cb93b60857f6a2e24f3a5608 100644 --- a/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php +++ b/tests/src/Functional/AutomaticUpdatesFunctionalTestBase.php @@ -10,6 +10,7 @@ use Drupal\fixture_manipulator\StageFixtureManipulator; use Drupal\package_manager_bypass\Beginner; use Drupal\package_manager_bypass\Stager; use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait; use Drupal\Tests\package_manager\Traits\FixtureUtilityTrait; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -20,6 +21,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; */ abstract class AutomaticUpdatesFunctionalTestBase extends BrowserTestBase { + use AssertPreconditionsTrait; use FixtureUtilityTrait; /** diff --git a/tests/src/Functional/HelpPageTest.php b/tests/src/Functional/HelpPageTest.php index bfe81a41b1fd1d43278992e3e9a791ab02e36db9..9e32ed8f8109751e0184b2a058a3613ac89ad7de 100644 --- a/tests/src/Functional/HelpPageTest.php +++ b/tests/src/Functional/HelpPageTest.php @@ -5,6 +5,7 @@ declare(strict_types = 1); namespace Drupal\Tests\automatic_updates\Functional; use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait; /** * @group automatic_updates @@ -12,6 +13,8 @@ use Drupal\Tests\BrowserTestBase; */ class HelpPageTest extends BrowserTestBase { + use AssertPreconditionsTrait; + /** * {@inheritdoc} */ diff --git a/tests/src/Functional/UpdatePathTest.php b/tests/src/Functional/UpdatePathTest.php index b697f42c18305137e9f370592b6bd0eb38d0747b..329e34fb1eae4b80bf169f9f5852470493eea0cd 100644 --- a/tests/src/Functional/UpdatePathTest.php +++ b/tests/src/Functional/UpdatePathTest.php @@ -7,6 +7,7 @@ namespace Drupal\Tests\automatic_updates\Functional; use Drupal\automatic_updates\CronUpdater; use Drupal\automatic_updates\StatusCheckMailer; use Drupal\FunctionalTests\Update\UpdatePathTestBase; +use Drupal\Tests\package_manager\Traits\AssertPreconditionsTrait; /** * @group automatic_updates @@ -14,6 +15,8 @@ use Drupal\FunctionalTests\Update\UpdatePathTestBase; */ class UpdatePathTest extends UpdatePathTestBase { + use AssertPreconditionsTrait; + /** * {@inheritdoc} */