mirror of
https://github.com/pestphp/pest.git
synced 2026-06-15 07:28:23 +02:00
Optimize buildFilterArgument in Shard plugin for compact regex generation and add comprehensive tests (#1675)
This commit is contained in:
@ -225,7 +225,38 @@ final class Shard implements AddsOutput, HandlesArguments, Terminable
|
|||||||
*/
|
*/
|
||||||
private function buildFilterArgument(array $testsToRun): string
|
private function buildFilterArgument(array $testsToRun): string
|
||||||
{
|
{
|
||||||
return addslashes(implode('|', $testsToRun));
|
if ($testsToRun === []) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var array<string, mixed> $tree */
|
||||||
|
$tree = [];
|
||||||
|
foreach ($testsToRun as $class) {
|
||||||
|
$parts = explode('\\', $class);
|
||||||
|
$current = &$tree;
|
||||||
|
foreach ($parts as $part) {
|
||||||
|
if (! isset($current[$part])) {
|
||||||
|
$current[$part] = [];
|
||||||
|
}
|
||||||
|
$current = &$current[$part];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$buildRegex = function (array $tree) use (&$buildRegex): string {
|
||||||
|
$parts = [];
|
||||||
|
foreach ($tree as $key => $sub) {
|
||||||
|
$subRegex = $buildRegex($sub);
|
||||||
|
if ($subRegex === '') {
|
||||||
|
$parts[] = preg_quote($key, '/');
|
||||||
|
} else {
|
||||||
|
$parts[] = preg_quote($key, '/').'\\\\'.(count($sub) > 1 ? '('.$subRegex.')' : $subRegex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode('|', $parts);
|
||||||
|
};
|
||||||
|
|
||||||
|
return $buildRegex($tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -50,6 +50,98 @@ describe('getShard', function () {
|
|||||||
])->throws(InvalidOption::class);
|
])->throws(InvalidOption::class);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('buildFilterArgument', function () {
|
||||||
|
it('generates compact filter for single test', function () {
|
||||||
|
$output = new BufferedOutput;
|
||||||
|
$shard = new Shard($output);
|
||||||
|
|
||||||
|
$reflection = new ReflectionClass($shard);
|
||||||
|
$method = $reflection->getMethod('buildFilterArgument');
|
||||||
|
|
||||||
|
$filter = $method->invoke($shard, ['Tests\\Unit\\ExampleTest']);
|
||||||
|
|
||||||
|
expect($filter)->toBe('Tests\\\\Unit\\\\ExampleTest');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('generates compact filter for multiple tests with common prefix', function () {
|
||||||
|
$output = new BufferedOutput;
|
||||||
|
$shard = new Shard($output);
|
||||||
|
|
||||||
|
$reflection = new ReflectionClass($shard);
|
||||||
|
$method = $reflection->getMethod('buildFilterArgument');
|
||||||
|
|
||||||
|
$filter = $method->invoke($shard, [
|
||||||
|
'Tests\\Unit\\Foo\\BarTest',
|
||||||
|
'Tests\\Unit\\Foo\\BazTest',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($filter)->toBe('Tests\\\\Unit\\\\Foo\\\\(BarTest|BazTest)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('generates compact filter for tests with different namespaces', function () {
|
||||||
|
$output = new BufferedOutput;
|
||||||
|
$shard = new Shard($output);
|
||||||
|
|
||||||
|
$reflection = new ReflectionClass($shard);
|
||||||
|
$method = $reflection->getMethod('buildFilterArgument');
|
||||||
|
|
||||||
|
$filter = $method->invoke($shard, [
|
||||||
|
'Tests\\Unit\\FooTest',
|
||||||
|
'Tests\\Feature\\BarTest',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($filter)->toBe('Tests\\\\(Unit\\\\FooTest|Feature\\\\BarTest)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty string for empty test list', function () {
|
||||||
|
$output = new BufferedOutput;
|
||||||
|
$shard = new Shard($output);
|
||||||
|
|
||||||
|
$reflection = new ReflectionClass($shard);
|
||||||
|
$method = $reflection->getMethod('buildFilterArgument');
|
||||||
|
|
||||||
|
$filter = $method->invoke($shard, []);
|
||||||
|
|
||||||
|
expect($filter)->toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('generates compact filter for deeply nested namespaces', function () {
|
||||||
|
$output = new BufferedOutput;
|
||||||
|
$shard = new Shard($output);
|
||||||
|
|
||||||
|
$reflection = new ReflectionClass($shard);
|
||||||
|
$method = $reflection->getMethod('buildFilterArgument');
|
||||||
|
|
||||||
|
$filter = $method->invoke($shard, [
|
||||||
|
'Tests\\Unit\\Plugins\\Concerns\\Foo',
|
||||||
|
'Tests\\Unit\\Plugins\\Concerns\\Bar',
|
||||||
|
'Tests\\Unit\\Plugins\\Concerns\\Baz',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($filter)->toBe('Tests\\\\Unit\\\\Plugins\\\\Concerns\\\\(Foo|Bar|Baz)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles mix of nested and flat namespaces', function () {
|
||||||
|
$output = new BufferedOutput;
|
||||||
|
$shard = new Shard($output);
|
||||||
|
|
||||||
|
$reflection = new ReflectionClass($shard);
|
||||||
|
$method = $reflection->getMethod('buildFilterArgument');
|
||||||
|
|
||||||
|
$tests = [
|
||||||
|
'Tests\\Unit\\SimpleTest',
|
||||||
|
'Tests\\Unit\\Plugins\\Concerns\\HandleArguments',
|
||||||
|
'Tests\\Unit\\Plugins\\Concerns\\Validation',
|
||||||
|
'Tests\\Unit\\Another\\Deep\\Nested\\Test',
|
||||||
|
];
|
||||||
|
|
||||||
|
$filter = $method->invoke($shard, $tests);
|
||||||
|
|
||||||
|
expect($filter)
|
||||||
|
->toBe(addslashes('Tests\\Unit\\(SimpleTest|Plugins\\Concerns\\(HandleArguments|Validation)|Another\\Deep\\Nested\\Test)'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('ensureFilterLengthIsSafe', function () {
|
describe('ensureFilterLengthIsSafe', function () {
|
||||||
it('accepts filter within length limit', function () {
|
it('accepts filter within length limit', function () {
|
||||||
$output = new BufferedOutput;
|
$output = new BufferedOutput;
|
||||||
|
|||||||
Reference in New Issue
Block a user