Skip to content

Commit

Permalink
feat: distinguish required and supported groups (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
simPod authored Mar 13, 2023
1 parent b5fa1c2 commit f7c6ad1
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 51 deletions.
106 changes: 68 additions & 38 deletions src/TestCheck/EveryTestHasGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,36 @@
namespace Cdn77\TestUtils\TestCheck;

use Cdn77\EntityFqnExtractor\ClassExtractor;
use InvalidArgumentException;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;
use ReflectionClass;

use function Safe\preg_match;
use function array_intersect;
use function array_map;
use function in_array;
use function Safe\preg_match_all;
use function sprintf;

final class EveryTestHasGroup implements TestCheck
{
/**
* @param iterable<string> $filePathNames
* @param list<string> $allowedGroups
* @param non-empty-list<string>|null $requiredGroups
* @param list<string> $supportedGroups
*/
public function __construct(private iterable $filePathNames, private array $allowedGroups)
{
public function __construct(
private iterable $filePathNames,
private array|null $requiredGroups = null,
private array|null $supportedGroups = null,
) {
if (
$requiredGroups !== null
&& $supportedGroups !== null
&& array_intersect($requiredGroups, $supportedGroups) !== []
) {
throw new InvalidArgumentException('Required groups must not be in supported groups');
}
}

public function run(TestCase $testCaseContext): void
Expand All @@ -31,48 +46,63 @@ public function run(TestCase $testCaseContext): void
}

$groupAttributes = $classReflection->getAttributes(Group::class);
foreach ($groupAttributes as $groupAttribute) {
if ($groupAttributes !== []) {
$groups = array_map(
static fn ($groupAttribute) => $groupAttribute->getArguments()[0],
$groupAttributes,
);
} else {
$docComment = $classReflection->getDocComment();
if ($docComment === false) {
$testCaseContext::fail(sprintf('Test "%s" is missing phpdoc comment', $classReflection->getName()));
}

if (preg_match_all('~\* @group +(?<group>\w+)(\n| \*/)~', $docComment, $matches) === 0) {
$testCaseContext::fail(
sprintf('Test "%s" is missing @group annotation', $classReflection->getName()),
);
}

$groups = $matches['group'];
}

$hasRequiredGroup = false;
foreach ($groups as $group) {
if (
$this->requiredGroups !== null
&& in_array($group, $this->requiredGroups, true)
) {
$hasRequiredGroup = true;

continue;
}

if ($this->supportedGroups === null) {
continue;
}

$testCaseContext::assertContains(
$groupAttribute->getArguments()[0],
$this->allowedGroups,
$group,
$this->supportedGroups,
sprintf(
'Test "%s" has invalid @group annotation "%s"',
'Test "%s" has invalid Group attribute "%s"',
$classReflection->getName(),
$groupAttribute->getArguments()[0],
$group,
),
);
}

if ($groupAttributes !== []) {
continue;
if ($this->requiredGroups !== null) {
$testCaseContext::assertTrue(
$hasRequiredGroup,
sprintf(
'Test "%s" does not have required Group attribute',
$classReflection->getName(),
),
);
} else {
$testCaseContext::assertTrue(true);
}

$this->validateDocComment($testCaseContext, $classReflection);
}
}

/** @param ReflectionClass<object> $reflectionClass */
private function validateDocComment(TestCase $testCaseContext, ReflectionClass $reflectionClass): void
{
$docComment = $reflectionClass->getDocComment();
if ($docComment === false) {
$testCaseContext::fail(sprintf('Test "%s" is missing phpdoc comment', $reflectionClass->getName()));
}

if (preg_match('~\* @group +(?<group>\w+)(\n| \*/)~', $docComment, $matches) !== 1) {
$testCaseContext::fail(
sprintf('Test "%s" is missing @group annotation', $reflectionClass->getName()),
);
}

$testCaseContext::assertContains(
$matches['group'],
$this->allowedGroups,
sprintf(
'Test "%s" has invalid @group annotation "%s"',
$reflectionClass->getName(),
$matches['group'],
),
);
}
}
49 changes: 40 additions & 9 deletions tests/TestCheck/EveryTestHasGroupTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,47 @@

final class EveryTestHasGroupTest extends BaseTestCase
{
public function testSuccess(): void
{
$check = new EveryTestHasGroup([__DIR__ . '/Fixtures/WithGroup.php'], ['unit']);
/**
* @param non-empty-list<string>|null $requiredGroups
* @param list<string>|null $supportedGroups
*
* @dataProvider providerSuccess
*/
public function testSuccess(
string $filePath,
array|null $requiredGroups,
array|null $supportedGroups,
): void {
$check = new EveryTestHasGroup([__DIR__ . '/Fixtures/' . $filePath], $requiredGroups, $supportedGroups);
$check->run($this);
}

/** @dataProvider providerFail */
public function testFail(string $filePath): void
/** @return Generator<string, array{string, non-empty-list<string>|null, list<string>|null}> */
public static function providerSuccess(): Generator
{
yield 'required, any supported' => ['WithGroups.php', ['a'], null];
yield 'required, supported' => ['WithGroups.php', ['a'], ['b']];
yield 'no required, supported' => ['WithGroups.php', null, ['a', 'b']];
yield 'no required, any supported' => ['WithGroups.php', null, null];
yield 'required, any supported - A' => ['WithGroupsAnnotations.php', ['a'], null];
yield 'required, supported - A' => ['WithGroupsAnnotations.php', ['a'], ['b']];
yield 'no required, supported - A' => ['WithGroupsAnnotations.php', null, ['a', 'b']];
yield 'no required, any supported - A' => ['WithGroupsAnnotations.php', null, null];
}

/**
* @param non-empty-list<string>|null $requiredGroups
* @param list<string>|null $supportedGroups
*
* @dataProvider providerFail
*/
public function testFail(
string $filePath,
array|null $requiredGroups,
array|null $supportedGroups,
): void {
try {
$check = new EveryTestHasGroup([__DIR__ . '/Fixtures/' . $filePath], ['unit']);
$check = new EveryTestHasGroup([__DIR__ . '/Fixtures/' . $filePath], $requiredGroups, $supportedGroups);
$check->run($this);
} catch (AssertionFailedError) {
return;
Expand All @@ -30,10 +60,11 @@ public function testFail(string $filePath): void
self::fail('Unexpected check outcome');
}

/** @return Generator<list<string>> */
/** @return Generator<string, array{string, non-empty-list<string>|null, list<string>|null}> */
public static function providerFail(): Generator
{
yield ['WithoutGroup.php'];
yield ['WithUnlistedGroup.php'];
yield 'has unsupported group, required' => ['WithGroups.php', null, []];
yield 'has no group, required' => ['WithoutGroup.php', ['a'], null];
yield 'has unsupported group, required - A' => ['WithGroupsAnnotations.php', null, []];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

use PHPUnit\Framework\Attributes\Group;

#[Group('unit')]
final class WithGroup
#[Group('a')]
#[Group('b')]
final class WithGroups
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@

use PHPUnit\Framework\Attributes\Group;

#[Group('Eheu, raptus advena!')]
final class WithUnlistedGroup
/**
* @group a
* @group b
*/
final class WithGroupsAnnotations
{
}

0 comments on commit f7c6ad1

Please sign in to comment.