Skip to content

Commit

Permalink
Priority for hook methods
Browse files Browse the repository at this point in the history
  • Loading branch information
nikophil authored and sebastianbergmann committed Jul 26, 2024
1 parent c787b2f commit cebecfb
Show file tree
Hide file tree
Showing 25 changed files with 802 additions and 125 deletions.
11 changes: 11 additions & 0 deletions src/Framework/Attributes/After.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,15 @@
#[Attribute(Attribute::TARGET_METHOD)]
final readonly class After
{
private int $priority;

public function __construct(int $priority = 0)
{
$this->priority = $priority;
}

public function priority(): int
{
return $this->priority;
}
}
11 changes: 11 additions & 0 deletions src/Framework/Attributes/AfterClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,15 @@
#[Attribute(Attribute::TARGET_METHOD)]
final readonly class AfterClass
{
private int $priority;

public function __construct(int $priority = 0)
{
$this->priority = $priority;
}

public function priority(): int
{
return $this->priority;
}
}
11 changes: 11 additions & 0 deletions src/Framework/Attributes/Before.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,15 @@
#[Attribute(Attribute::TARGET_METHOD)]
final readonly class Before
{
private int $priority;

public function __construct(int $priority = 0)
{
$this->priority = $priority;
}

public function priority(): int
{
return $this->priority;
}
}
11 changes: 11 additions & 0 deletions src/Framework/Attributes/BeforeClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,15 @@
#[Attribute(Attribute::TARGET_METHOD)]
final readonly class BeforeClass
{
private int $priority;

public function __construct(int $priority = 0)
{
$this->priority = $priority;
}

public function priority(): int
{
return $this->priority;
}
}
11 changes: 11 additions & 0 deletions src/Framework/Attributes/PostCondition.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,15 @@
#[Attribute(Attribute::TARGET_METHOD)]
final readonly class PostCondition
{
private int $priority;

public function __construct(int $priority = 0)
{
$this->priority = $priority;
}

public function priority(): int
{
return $this->priority;
}
}
11 changes: 11 additions & 0 deletions src/Framework/Attributes/PreCondition.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,15 @@
#[Attribute(Attribute::TARGET_METHOD)]
final readonly class PreCondition
{
private int $priority;

public function __construct(int $priority = 0)
{
$this->priority = $priority;
}

public function priority(): int
{
return $this->priority;
}
}
23 changes: 11 additions & 12 deletions src/Framework/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
use PHPUnit\Framework\TestStatus\TestStatus;
use PHPUnit\Metadata\Api\Groups;
use PHPUnit\Metadata\Api\HookMethods;
use PHPUnit\Metadata\Api\HookMethodsCollection;
use PHPUnit\Metadata\Api\Requirements;
use PHPUnit\Metadata\Parser\Registry as MetadataRegistry;
use PHPUnit\Runner\DeprecationCollector\Facade as DeprecationCollector;
Expand Down Expand Up @@ -2294,7 +2295,7 @@ private function performAssertionsOnOutput(): void
}

/**
* @param array{beforeClass: list<non-empty-string>, before: list<non-empty-string>, preCondition: list<non-empty-string>, postCondition: list<non-empty-string>, after: list<non-empty-string>, afterClass: list<non-empty-string>} $hookMethods
* @param array{beforeClass: HookMethodsCollection, before: HookMethodsCollection, preCondition: HookMethodsCollection, postCondition: HookMethodsCollection, after: HookMethodsCollection, afterClass: HookMethodsCollection} $hookMethods
*
* @throws Throwable
*
Expand All @@ -2311,7 +2312,7 @@ private function invokeBeforeClassHookMethods(array $hookMethods, Event\Emitter
}

/**
* @param array{beforeClass: list<non-empty-string>, before: list<non-empty-string>, preCondition: list<non-empty-string>, postCondition: list<non-empty-string>, after: list<non-empty-string>, afterClass: list<non-empty-string>} $hookMethods
* @param array{beforeClass: HookMethodsCollection, before: HookMethodsCollection, preCondition: HookMethodsCollection, postCondition: HookMethodsCollection, after: HookMethodsCollection, afterClass: HookMethodsCollection} $hookMethods
*
* @throws Throwable
*/
Expand All @@ -2326,7 +2327,7 @@ private function invokeBeforeTestHookMethods(array $hookMethods, Event\Emitter $
}

/**
* @param array{beforeClass: list<non-empty-string>, before: list<non-empty-string>, preCondition: list<non-empty-string>, postCondition: list<non-empty-string>, after: list<non-empty-string>, afterClass: list<non-empty-string>} $hookMethods
* @param array{beforeClass: HookMethodsCollection, before: HookMethodsCollection, preCondition: HookMethodsCollection, postCondition: HookMethodsCollection, after: HookMethodsCollection, afterClass: HookMethodsCollection} $hookMethods
*
* @throws Throwable
*/
Expand All @@ -2341,7 +2342,7 @@ private function invokePreConditionHookMethods(array $hookMethods, Event\Emitter
}

/**
* @param array{beforeClass: list<non-empty-string>, before: list<non-empty-string>, preCondition: list<non-empty-string>, postCondition: list<non-empty-string>, after: list<non-empty-string>, afterClass: list<non-empty-string>} $hookMethods
* @param array{beforeClass: HookMethodsCollection, before: HookMethodsCollection, preCondition: HookMethodsCollection, postCondition: HookMethodsCollection, after: HookMethodsCollection, afterClass: HookMethodsCollection} $hookMethods
*
* @throws Throwable
*/
Expand All @@ -2356,7 +2357,7 @@ private function invokePostConditionHookMethods(array $hookMethods, Event\Emitte
}

/**
* @param array{beforeClass: list<non-empty-string>, before: list<non-empty-string>, preCondition: list<non-empty-string>, postCondition: list<non-empty-string>, after: list<non-empty-string>, afterClass: list<non-empty-string>} $hookMethods
* @param array{beforeClass: HookMethodsCollection, before: HookMethodsCollection, preCondition: HookMethodsCollection, postCondition: HookMethodsCollection, after: HookMethodsCollection, afterClass: HookMethodsCollection} $hookMethods
*
* @throws Throwable
*/
Expand All @@ -2371,7 +2372,7 @@ private function invokeAfterTestHookMethods(array $hookMethods, Event\Emitter $e
}

/**
* @param array{beforeClass: list<non-empty-string>, before: list<non-empty-string>, preCondition: list<non-empty-string>, postCondition: list<non-empty-string>, after: list<non-empty-string>, afterClass: list<non-empty-string>} $hookMethods
* @param array{beforeClass: HookMethodsCollection, before: HookMethodsCollection, preCondition: HookMethodsCollection, postCondition: HookMethodsCollection, after: HookMethodsCollection, afterClass: HookMethodsCollection} $hookMethods
*
* @throws Throwable
*
Expand All @@ -2388,18 +2389,16 @@ private function invokeAfterClassHookMethods(array $hookMethods, Event\Emitter $
}

/**
* @param array{beforeClass: list<non-empty-string>, before: list<non-empty-string>, preCondition: list<non-empty-string>, postCondition: list<non-empty-string>, after: list<non-empty-string>, afterClass: list<non-empty-string>} $hookMethods
* @param 'testAfterLastTestMethodCalled'|'testAfterTestMethodCalled'|'testBeforeFirstTestMethodCalled'|'testBeforeTestMethodCalled'|'testPostConditionCalled'|'testPreConditionCalled' $calledMethod
* @param 'testAfterLastTestMethodFinished'|'testAfterTestMethodFinished'|'testBeforeFirstTestMethodFinished'|'testBeforeTestMethodFinished'|'testPostConditionFinished'|'testPreConditionFinished' $finishedMethod
* @param list<non-empty-string> $hookMethods
* @param 'testAfterLastTestMethodCalled'|'testAfterTestMethodCalled'|'testBeforeFirstTestMethodCalled'|'testBeforeTestMethodCalled'|'testPostConditionCalled'|'testPreConditionCalled' $calledMethod
* @param 'testAfterLastTestMethodFinished'|'testAfterTestMethodFinished'|'testBeforeFirstTestMethodFinished'|'testBeforeTestMethodFinished'|'testPostConditionFinished'|'testPreConditionFinished' $finishedMethod
*
* @throws Throwable
*/
private function invokeHookMethods(array $hookMethods, Event\Emitter $emitter, string $calledMethod, string $finishedMethod): void
private function invokeHookMethods(HookMethodsCollection $hookMethodsCollection, Event\Emitter $emitter, string $calledMethod, string $finishedMethod): void
{
$methodsInvoked = [];

foreach ($hookMethods as $methodName) {
foreach ($hookMethodsCollection as $methodName) {
if ($this->methodDoesNotExistOrIsDeclaredInTestCase($methodName)) {
continue;
}
Expand Down
17 changes: 17 additions & 0 deletions src/Metadata/After.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,25 @@
*/
final readonly class After extends Metadata
{
private int $priority;

/**
* @param 0|1 $level
*/
protected function __construct(int $level, int $priority)
{
parent::__construct($level);

$this->priority = $priority;
}

public function isAfter(): bool
{
return true;
}

public function priority(): int
{
return $this->priority;
}
}
17 changes: 17 additions & 0 deletions src/Metadata/AfterClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,25 @@
*/
final readonly class AfterClass extends Metadata
{
private int $priority;

/**
* @param 0|1 $level
*/
protected function __construct(int $level, int $priority)
{
parent::__construct($level);

$this->priority = $priority;
}

public function isAfterClass(): bool
{
return true;
}

public function priority(): int
{
return $this->priority;
}
}
24 changes: 24 additions & 0 deletions src/Metadata/Api/HookMethod.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Metadata\Api;

/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final readonly class HookMethod
{
public function __construct(
public string $methodName,
public int $priority = 0,
) {
}
}
70 changes: 48 additions & 22 deletions src/Metadata/Api/HookMethods.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@
*/
namespace PHPUnit\Metadata\Api;

use function array_unshift;
use function assert;
use function class_exists;
use PHPUnit\Framework\TestCase;
use PHPUnit\Metadata\After;
use PHPUnit\Metadata\AfterClass;
use PHPUnit\Metadata\Before;
use PHPUnit\Metadata\BeforeClass;
use PHPUnit\Metadata\Parser\Registry;
use PHPUnit\Metadata\PostCondition;
use PHPUnit\Metadata\PreCondition;
use PHPUnit\Util\Reflection;
use ReflectionClass;

Expand All @@ -23,14 +28,14 @@
final class HookMethods
{
/**
* @var array<class-string, array{beforeClass: list<non-empty-string>, before: list<non-empty-string>, preCondition: list<non-empty-string>, postCondition: list<non-empty-string>, after: list<non-empty-string>, afterClass: list<non-empty-string>}>
* @var array<class-string, array{beforeClass: HookMethodsCollection, before: HookMethodsCollection, preCondition: HookMethodsCollection, postCondition: HookMethodsCollection, after: HookMethodsCollection, afterClass: HookMethodsCollection}>
*/
private static array $hookMethods = [];

/**
* @param class-string<TestCase> $className
*
* @return array{beforeClass: list<non-empty-string>, before: list<non-empty-string>, preCondition: list<non-empty-string>, postCondition: list<non-empty-string>, after: list<non-empty-string>, afterClass: list<non-empty-string>}
* @return array{beforeClass: HookMethodsCollection, before: HookMethodsCollection, preCondition: HookMethodsCollection, postCondition: HookMethodsCollection, after: HookMethodsCollection, afterClass: HookMethodsCollection}
*/
public function hookMethods(string $className): array
{
Expand All @@ -53,55 +58,76 @@ public function hookMethods(string $className): array

if ($method->isStatic()) {
if ($metadata->isBeforeClass()->isNotEmpty()) {
array_unshift(
self::$hookMethods[$className]['beforeClass'],
$methodName,
$beforeClass = $metadata->isBeforeClass()->asArray()[0];
assert($beforeClass instanceof BeforeClass);

self::$hookMethods[$className]['beforeClass']->add(
new HookMethod($methodName, $beforeClass->priority()),
);
}

if ($metadata->isAfterClass()->isNotEmpty()) {
self::$hookMethods[$className]['afterClass'][] = $methodName;
$afterClass = $metadata->isAfterClass()->asArray()[0];
assert($afterClass instanceof AfterClass);

self::$hookMethods[$className]['afterClass']->add(
new HookMethod($methodName, $afterClass->priority()),
);
}
}

if ($metadata->isBefore()->isNotEmpty()) {
array_unshift(
self::$hookMethods[$className]['before'],
$methodName,
$before = $metadata->isBefore()->asArray()[0];
assert($before instanceof Before);

self::$hookMethods[$className]['before']->add(
new HookMethod($methodName, $before->priority()),
);
}

if ($metadata->isPreCondition()->isNotEmpty()) {
array_unshift(
self::$hookMethods[$className]['preCondition'],
$methodName,
$preCondition = $metadata->isPreCondition()->asArray()[0];
assert($preCondition instanceof PreCondition);

self::$hookMethods[$className]['preCondition']->add(
new HookMethod($methodName, $preCondition->priority()),
);
}

if ($metadata->isPostCondition()->isNotEmpty()) {
self::$hookMethods[$className]['postCondition'][] = $methodName;
$postCondition = $metadata->isPostCondition()->asArray()[0];
assert($postCondition instanceof PostCondition);

self::$hookMethods[$className]['postCondition']->add(
new HookMethod($methodName, $postCondition->priority()),
);
}

if ($metadata->isAfter()->isNotEmpty()) {
self::$hookMethods[$className]['after'][] = $methodName;
$after = $metadata->isAfter()->asArray()[0];
assert($after instanceof After);

self::$hookMethods[$className]['after']->add(
new HookMethod($methodName, $after->priority()),
);
}
}

return self::$hookMethods[$className];
}

/**
* @return array{beforeClass: list<non-empty-string>, before: list<non-empty-string>, preCondition: list<non-empty-string>, postCondition: list<non-empty-string>, after: list<non-empty-string>, afterClass: list<non-empty-string>}
* @return array{beforeClass: HookMethodsCollection, before: HookMethodsCollection, preCondition: HookMethodsCollection, postCondition: HookMethodsCollection, after: HookMethodsCollection, afterClass: HookMethodsCollection}
*/
private function emptyHookMethodsArray(): array
{
return [
'beforeClass' => ['setUpBeforeClass'],
'before' => ['setUp'],
'preCondition' => ['assertPreConditions'],
'postCondition' => ['assertPostConditions'],
'after' => ['tearDown'],
'afterClass' => ['tearDownAfterClass'],
'beforeClass' => HookMethodsCollection::defaultBeforeClass(),
'before' => HookMethodsCollection::defaultBefore(),
'preCondition' => HookMethodsCollection::defaultPreCondition(),
'postCondition' => HookMethodsCollection::defaultPostCondition(),
'after' => HookMethodsCollection::defaultAfter(),
'afterClass' => HookMethodsCollection::defaultAfterClass(),
];
}
}
Loading

0 comments on commit cebecfb

Please sign in to comment.