Skip to content

Commit d35e14f

Browse files
nikophilsebastianbergmann
authored andcommitted
feat: #[RequiresEnvironmentVariable]
1 parent 8c85f60 commit d35e14f

File tree

13 files changed

+457
-1
lines changed

13 files changed

+457
-1
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\Framework\Attributes;
11+
12+
use Attribute;
13+
14+
/**
15+
* @immutable
16+
*
17+
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
18+
*/
19+
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
20+
final readonly class RequiresEnvironmentVariable
21+
{
22+
private string $environmentVariableName;
23+
private null|bool|int|string $value;
24+
25+
public function __construct(string $environmentVariableName, null|bool|int|string $value = null)
26+
{
27+
$this->environmentVariableName = $environmentVariableName;
28+
$this->value = $value;
29+
}
30+
31+
public function environmentVariableName(): string
32+
{
33+
return $this->environmentVariableName;
34+
}
35+
36+
public function value(): null|bool|int|string
37+
{
38+
return $this->value;
39+
}
40+
}

src/Metadata/Api/Requirements.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use const PHP_VERSION;
1515
use function addcslashes;
1616
use function array_column;
17+
use function array_key_exists;
1718
use function assert;
1819
use function extension_loaded;
1920
use function function_exists;
@@ -24,6 +25,7 @@
2425
use function preg_match;
2526
use function sprintf;
2627
use PHPUnit\Metadata\Parser\Registry;
28+
use PHPUnit\Metadata\RequiresEnvironmentVariable;
2729
use PHPUnit\Metadata\RequiresFunction;
2830
use PHPUnit\Metadata\RequiresMethod;
2931
use PHPUnit\Metadata\RequiresOperatingSystem;
@@ -105,6 +107,24 @@ public function requirementsNotSatisfiedFor(string $className, string $methodNam
105107
}
106108
}
107109

110+
if ($metadata->isRequiresEnvironmentVariable()) {
111+
assert($metadata instanceof RequiresEnvironmentVariable);
112+
113+
if (!array_key_exists($metadata->environmentVariableName(), $_ENV)) {
114+
$notSatisfied[] = sprintf('Environment variable "%s" is required.', $metadata->environmentVariableName());
115+
116+
continue;
117+
}
118+
119+
if ($metadata->value() !== null && $_ENV[$metadata->environmentVariableName()] !== $metadata->value()) {
120+
$notSatisfied[] = sprintf(
121+
'Environment variable "%s" is required to be "%s".',
122+
$metadata->environmentVariableName(),
123+
$metadata->value(),
124+
);
125+
}
126+
}
127+
108128
if ($metadata->isRequiresOperatingSystemFamily()) {
109129
assert($metadata instanceof RequiresOperatingSystemFamily);
110130

src/Metadata/Metadata.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,16 @@ public static function requiresPhpunitExtensionOnMethod(string $extensionClass):
391391
return new RequiresPhpunitExtension(self::METHOD_LEVEL, $extensionClass);
392392
}
393393

394+
public static function requiresEnvironmentVariableOnClass(string $environmentVariableName, null|int|string $value): RequiresEnvironmentVariable
395+
{
396+
return new RequiresEnvironmentVariable(self::CLASS_LEVEL, $environmentVariableName, $value);
397+
}
398+
399+
public static function requiresEnvironmentVariableOnMethod(string $environmentVariableName, null|int|string $value): RequiresEnvironmentVariable
400+
{
401+
return new RequiresEnvironmentVariable(self::METHOD_LEVEL, $environmentVariableName, $value);
402+
}
403+
394404
/**
395405
* @param non-empty-string $setting
396406
* @param non-empty-string $value
@@ -816,6 +826,14 @@ public function isRequiresPhpunitExtension(): bool
816826
return false;
817827
}
818828

829+
/**
830+
* @phpstan-assert-if-true RequiresEnvironmentVariable $this
831+
*/
832+
public function isRequiresEnvironmentVariable(): bool
833+
{
834+
return false;
835+
}
836+
819837
/**
820838
* @phpstan-assert-if-true RequiresSetting $this
821839
*/

src/Metadata/MetadataCollection.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,16 @@ public function isRequiresPhpunitExtension(): self
483483
);
484484
}
485485

486+
public function isRequiresEnvironmentVariable(): self
487+
{
488+
return new self(
489+
...array_filter(
490+
$this->metadata,
491+
static fn (Metadata $metadata): bool => $metadata->isRequiresEnvironmentVariable(),
492+
),
493+
);
494+
}
495+
486496
public function isRequiresSetting(): self
487497
{
488498
return new self(

src/Metadata/Parser/AttributeParser.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
use PHPUnit\Framework\Attributes\PostCondition;
5555
use PHPUnit\Framework\Attributes\PreCondition;
5656
use PHPUnit\Framework\Attributes\PreserveGlobalState;
57+
use PHPUnit\Framework\Attributes\RequiresEnvironmentVariable;
5758
use PHPUnit\Framework\Attributes\RequiresFunction;
5859
use PHPUnit\Framework\Attributes\RequiresMethod;
5960
use PHPUnit\Framework\Attributes\RequiresOperatingSystem;
@@ -315,6 +316,16 @@ public function forClass(string $className): MetadataCollection
315316

316317
break;
317318

319+
case RequiresEnvironmentVariable::class:
320+
assert($attributeInstance instanceof RequiresEnvironmentVariable);
321+
322+
$result[] = Metadata::requiresEnvironmentVariableOnClass(
323+
$attributeInstance->environmentVariableName(),
324+
$attributeInstance->value(),
325+
);
326+
327+
break;
328+
318329
case RequiresSetting::class:
319330
assert($attributeInstance instanceof RequiresSetting);
320331

@@ -691,6 +702,16 @@ public function forMethod(string $className, string $methodName): MetadataCollec
691702

692703
break;
693704

705+
case RequiresEnvironmentVariable::class:
706+
assert($attributeInstance instanceof RequiresEnvironmentVariable);
707+
708+
$result[] = Metadata::requiresEnvironmentVariableOnMethod(
709+
$attributeInstance->environmentVariableName(),
710+
$attributeInstance->value(),
711+
);
712+
713+
break;
714+
694715
case RequiresSetting::class:
695716
assert($attributeInstance instanceof RequiresSetting);
696717

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\Metadata;
11+
12+
/**
13+
* @immutable
14+
*
15+
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
16+
*/
17+
final readonly class RequiresEnvironmentVariable extends Metadata
18+
{
19+
private string $environmentVariableName;
20+
private null|bool|int|string $value;
21+
22+
public function __construct(int $level, string $environmentVariableName, null|bool|int|string $value)
23+
{
24+
parent::__construct($level);
25+
26+
$this->environmentVariableName = $environmentVariableName;
27+
$this->value = $value;
28+
}
29+
30+
public function isRequiresEnvironmentVariable(): true
31+
{
32+
return true;
33+
}
34+
35+
public function environmentVariableName(): string
36+
{
37+
return $this->environmentVariableName;
38+
}
39+
40+
public function value(): null|bool|int|string
41+
{
42+
return $this->value;
43+
}
44+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\TestFixture\Metadata\Attribute;
11+
12+
use PHPUnit\Framework\Attributes\RequiresEnvironmentVariable;
13+
use PHPUnit\Framework\TestCase;
14+
15+
#[RequiresEnvironmentVariable('foo', 'bar')]
16+
final class RequiresEnvironmentVariableTest extends TestCase
17+
{
18+
#[RequiresEnvironmentVariable('foo')]
19+
#[RequiresEnvironmentVariable('bar', 'baz')]
20+
public function testOne(): void
21+
{
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
declare(strict_types=1);
3+
/*
4+
* This file is part of PHPUnit.
5+
*
6+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
namespace PHPUnit\TestFixture;
12+
13+
use PHPUnit\Framework\Attributes\RequiresEnvironmentVariable;
14+
use PHPUnit\Framework\TestCase;
15+
16+
final class RequirementsEnvironmentVariableTest extends TestCase
17+
{
18+
#[RequiresEnvironmentVariable('foo', 'bar')]
19+
#[RequiresEnvironmentVariable('baz')]
20+
public function testRequiresEnvironmentVariable(): void
21+
{
22+
}
23+
}

tests/unit/Metadata/Api/RequirementsTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\Attributes\Group;
1515
use PHPUnit\Framework\Attributes\Small;
1616
use PHPUnit\Framework\TestCase;
17+
use PHPUnit\TestFixture\RequirementsEnvironmentVariableTest;
1718

1819
#[CoversClass(Requirements::class)]
1920
#[Small]
@@ -130,6 +131,12 @@ public static function missingRequirementsProvider(): array
130131
];
131132
}
132133

134+
protected function tearDown(): void
135+
{
136+
parent::tearDown();
137+
unset($_ENV['foo']);
138+
}
139+
133140
#[DataProvider('missingRequirementsProvider')]
134141
public function testGetMissingRequirements(string $test, array $result): void
135142
{
@@ -138,4 +145,17 @@ public function testGetMissingRequirements(string $test, array $result): void
138145
(new Requirements)->requirementsNotSatisfiedFor(\PHPUnit\TestFixture\RequirementsTest::class, $test),
139146
);
140147
}
148+
149+
public function testGetMissingEnvironmentVariableRequirements(): void
150+
{
151+
$_ENV['foo'] = '';
152+
153+
$this->assertEquals(
154+
[
155+
'Environment variable "foo" is required to be "bar".',
156+
'Environment variable "baz" is required.',
157+
],
158+
(new Requirements)->requirementsNotSatisfiedFor(RequirementsEnvironmentVariableTest::class, 'testRequiresEnvironmentVariable'),
159+
);
160+
}
141161
}

tests/unit/Metadata/MetadataCollectionTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#[UsesClass(RequiresPhpExtension::class)]
4949
#[UsesClass(RequiresPhpunit::class)]
5050
#[UsesClass(RequiresPhpunitExtension::class)]
51+
#[UsesClass(RequiresEnvironmentVariable::class)]
5152
#[UsesClass(RequiresSetting::class)]
5253
#[UsesClass(RunClassInSeparateProcess::class)]
5354
#[UsesClass(RunInSeparateProcess::class)]
@@ -402,6 +403,14 @@ public function test_Can_be_filtered_for_RequiresPhpunitExtension(): void
402403
$this->assertTrue($collection->asArray()[0]->isRequiresPhpunitExtension());
403404
}
404405

406+
public function test_Can_be_filtered_for_RequiresEnvironmentVariable(): void
407+
{
408+
$collection = $this->collectionWithOneOfEach()->isRequiresEnvironmentVariable();
409+
410+
$this->assertCount(1, $collection);
411+
$this->assertTrue($collection->asArray()[0]->isRequiresEnvironmentVariable());
412+
}
413+
405414
public function test_Can_be_filtered_for_RequiresSetting(): void
406415
{
407416
$collection = $this->collectionWithOneOfEach()->isRequiresSetting();
@@ -553,6 +562,7 @@ private function collectionWithOneOfEach(): MetadataCollection
553562
),
554563
),
555564
Metadata::requiresPhpunitExtensionOnClass(stdClass::class),
565+
Metadata::requiresEnvironmentVariableOnClass('foo', 'bar'),
556566
Metadata::requiresSettingOnClass('foo', 'bar'),
557567
Metadata::runClassInSeparateProcess(),
558568
Metadata::runInSeparateProcess(),

0 commit comments

Comments
 (0)