Skip to content

Commit

Permalink
#[RequiresPhpunitExtension] attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
nikophil authored and sebastianbergmann committed Aug 26, 2024
1 parent 93961d3 commit bf3d4a4
Show file tree
Hide file tree
Showing 13 changed files with 437 additions and 0 deletions.
43 changes: 43 additions & 0 deletions src/Framework/Attributes/RequiresPhpunitExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\Attributes;

use Attribute;
use PHPUnit\Runner\Extension\Extension;

/**
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*/
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
final readonly class RequiresPhpunitExtension
{
/**
* @var class-string<Extension>
*/
private string $extensionClasses;

/**
* @param class-string<Extension> $extensionClas
*/
public function __construct(string $extensionClas)
{
$this->extensionClasses = $extensionClas;
}

/**
* @return class-string<Extension>
*/
public function extensionClass(): string
{
return $this->extensionClasses;
}
}
19 changes: 19 additions & 0 deletions src/Metadata/Api/Requirements.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
use const PHP_OS_FAMILY;
use const PHP_VERSION;
use function addcslashes;
use function array_column;
use function assert;
use function extension_loaded;
use function function_exists;
use function in_array;
use function ini_get;
use function method_exists;
use function phpversion;
Expand All @@ -29,8 +31,10 @@
use PHPUnit\Metadata\RequiresPhp;
use PHPUnit\Metadata\RequiresPhpExtension;
use PHPUnit\Metadata\RequiresPhpunit;
use PHPUnit\Metadata\RequiresPhpunitExtension;
use PHPUnit\Metadata\RequiresSetting;
use PHPUnit\Runner\Version;
use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry;

/**
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
Expand Down Expand Up @@ -86,6 +90,21 @@ public function requirementsNotSatisfiedFor(string $className, string $methodNam
}
}

if ($metadata->isRequiresPhpunitExtension()) {
assert($metadata instanceof RequiresPhpunitExtension);

$configuration = ConfigurationRegistry::get();

$extensionBootstrappers = array_column($configuration->extensionBootstrappers(), 'className');

if ($configuration->noExtensions() || !in_array($metadata->extensionClass(), $extensionBootstrappers, true)) {
$notSatisfied[] = sprintf(
'PHPUnit extension "%s" is required.',
$metadata->extensionClass(),
);
}
}

if ($metadata->isRequiresOperatingSystemFamily()) {
assert($metadata instanceof RequiresOperatingSystemFamily);

Expand Down
25 changes: 25 additions & 0 deletions src/Metadata/Metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace PHPUnit\Metadata;

use PHPUnit\Metadata\Version\Requirement;
use PHPUnit\Runner\Extension\Extension;

/**
* @immutable
Expand Down Expand Up @@ -390,6 +391,22 @@ public static function requiresPhpunitOnMethod(Requirement $versionRequirement):
return new RequiresPhpunit(self::METHOD_LEVEL, $versionRequirement);
}

/**
* @param class-string<Extension> $extensionClass
*/
public static function requiresPhpunitExtensionOnClass(string $extensionClass): RequiresPhpunitExtension
{
return new RequiresPhpunitExtension(self::CLASS_LEVEL, $extensionClass);
}

/**
* @param class-string<Extension> $extensionClass
*/
public static function requiresPhpunitExtensionOnMethod(string $extensionClass): RequiresPhpunitExtension
{
return new RequiresPhpunitExtension(self::METHOD_LEVEL, $extensionClass);
}

/**
* @param non-empty-string $setting
* @param non-empty-string $value
Expand Down Expand Up @@ -831,6 +848,14 @@ public function isRequiresPhpunit(): bool
return false;
}

/**
* @phpstan-assert-if-true RequiresPhpunitExtension $this
*/
public function isRequiresPhpunitExtension(): bool
{
return false;
}

/**
* @phpstan-assert-if-true RequiresSetting $this
*/
Expand Down
10 changes: 10 additions & 0 deletions src/Metadata/MetadataCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,16 @@ public function isRequiresPhpunit(): self
);
}

public function isRequiresPhpunitExtension(): self
{
return new self(
...array_filter(
$this->metadata,
static fn (Metadata $metadata): bool => $metadata->isRequiresPhpunitExtension(),
),
);
}

public function isRequiresSetting(): self
{
return new self(
Expand Down
19 changes: 19 additions & 0 deletions src/Metadata/Parser/AttributeParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
use PHPUnit\Framework\Attributes\RequiresPhp;
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use PHPUnit\Framework\Attributes\RequiresPhpunit;
use PHPUnit\Framework\Attributes\RequiresPhpunitExtension;
use PHPUnit\Framework\Attributes\RequiresSetting;
use PHPUnit\Framework\Attributes\RunClassInSeparateProcess;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
Expand Down Expand Up @@ -288,6 +289,15 @@ public function forClass(string $className): MetadataCollection

break;

case RequiresPhpunitExtension::class:
assert($attributeInstance instanceof RequiresPhpunitExtension);

$result[] = Metadata::requiresPhpunitExtensionOnClass(
$attributeInstance->extensionClass(),
);

break;

case RequiresSetting::class:
assert($attributeInstance instanceof RequiresSetting);

Expand Down Expand Up @@ -641,6 +651,15 @@ public function forMethod(string $className, string $methodName): MetadataCollec

break;

case RequiresPhpunitExtension::class:
assert($attributeInstance instanceof RequiresPhpunitExtension);

$result[] = Metadata::requiresPhpunitExtensionOnMethod(
$attributeInstance->extensionClass(),
);

break;

case RequiresSetting::class:
assert($attributeInstance instanceof RequiresSetting);

Expand Down
48 changes: 48 additions & 0 deletions src/Metadata/RequiresPhpunitExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Metadata;

use PHPUnit\Runner\Extension\Extension;

/**
* @immutable
*
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*/
final readonly class RequiresPhpunitExtension extends Metadata
{
/**
* @var class-string<Extension>
*/
private string $extensionClass;

/**
* @param class-string<Extension> $extensionClass
*/
public function __construct(int $level, string $extensionClass)
{
parent::__construct($level);

$this->extensionClass = $extensionClass;
}

public function isRequiresPhpunitExtension(): true
{
return true;
}

/**
* @return class-string<Extension>
*/
public function extensionClass(): string
{
return $this->extensionClass;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TestFixture\Metadata\Attribute;

use PHPUnit\Framework\Attributes\RequiresPhpunitExtension;
use PHPUnit\Framework\TestCase;

#[RequiresPhpunitExtension(SomeExtension::class)]
final class RequiresPhpunitExtensionTest extends TestCase
{
#[RequiresPhpunitExtension(SomeExtension::class)]
#[RequiresPhpunitExtension(SomeOtherExtension::class)]
public function testOne(): void
{
}
}
7 changes: 7 additions & 0 deletions tests/_files/RequirementsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use PHPUnit\Framework\Attributes\RequiresPhp;
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use PHPUnit\Framework\Attributes\RequiresPhpunit;
use PHPUnit\Framework\Attributes\RequiresPhpunitExtension;
use PHPUnit\Framework\Attributes\RequiresSetting;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\Attributes\Ticket;
Expand Down Expand Up @@ -363,4 +364,10 @@ public function testVersionConstraintRegexpIgnoresWhitespace(): void
public function testSettingDisplayErrorsOn(): void
{
}

#[RequiresPhpunitExtension(SomeExtension::class)]
#[RequiresPhpunitExtension(SomeOtherExtension::class)]
public function testPHPUnitExtensionRequired(): void
{
}
}
4 changes: 4 additions & 0 deletions tests/unit/Metadata/Api/RequirementsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ public static function missingRequirementsProvider(): array
'PHP ^1.0 is required.',
'PHPUnit ^2.0 is required.',
]],
['testPHPUnitExtensionRequired', [
'PHPUnit extension "PHPUnit\TestFixture\SomeExtension" is required.',
'PHPUnit extension "PHPUnit\TestFixture\SomeOtherExtension" is required.',
]],
];
}

Expand Down
11 changes: 11 additions & 0 deletions tests/unit/Metadata/MetadataCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use PHPUnit\Framework\TestCase;
use PHPUnit\Metadata\Version\ComparisonRequirement;
use PHPUnit\Util\VersionComparisonOperator;
use stdClass;

#[CoversClass(MetadataCollection::class)]
#[CoversClass(MetadataCollectionIterator::class)]
Expand Down Expand Up @@ -48,6 +49,7 @@
#[UsesClass(RequiresPhp::class)]
#[UsesClass(RequiresPhpExtension::class)]
#[UsesClass(RequiresPhpunit::class)]
#[UsesClass(RequiresPhpunitExtension::class)]
#[UsesClass(RequiresSetting::class)]
#[UsesClass(RunClassInSeparateProcess::class)]
#[UsesClass(RunInSeparateProcess::class)]
Expand Down Expand Up @@ -404,6 +406,14 @@ public function test_Can_be_filtered_for_RequiresPhpunit(): void
$this->assertTrue($collection->asArray()[0]->isRequiresPhpunit());
}

public function test_Can_be_filtered_for_RequiresPhpunitExtension(): void
{
$collection = $this->collectionWithOneOfEach()->isRequiresPhpunitExtension();

$this->assertCount(1, $collection);
$this->assertTrue($collection->asArray()[0]->isRequiresPhpunitExtension());
}

public function test_Can_be_filtered_for_RequiresSetting(): void
{
$collection = $this->collectionWithOneOfEach()->isRequiresSetting();
Expand Down Expand Up @@ -563,6 +573,7 @@ private function collectionWithOneOfEach(): MetadataCollection
new VersionComparisonOperator('>='),
),
),
Metadata::requiresPhpunitExtensionOnClass(stdClass::class),
Metadata::requiresSettingOnClass('foo', 'bar'),
Metadata::runClassInSeparateProcess(),
Metadata::runInSeparateProcess(),
Expand Down
Loading

0 comments on commit bf3d4a4

Please sign in to comment.