Merge pull request #619 from pestphp/dirty_integration

[2.x] Adds initial implementation of `--dirty` option.
This commit is contained in:
Nuno Maduro
2023-01-10 21:34:24 +00:00
committed by GitHub
5 changed files with 108 additions and 28 deletions

View File

@ -3,8 +3,10 @@
use Pest\Actions\ValidatesEnvironment;
use Pest\ConfigLoader;
use Pest\Repositories\TestRepository;
use Pest\Support\Container;
use Pest\Kernel;
use Pest\Support\DirtyTestCaseFilter;
use Pest\TestSuite;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
@ -44,9 +46,18 @@ use Symfony\Component\Console\Output\OutputInterface;
$rootPath = dirname($autoloadPath, 2);
$argv = new ArgvInput();
$filters = [];
foreach ($args as $key => $value) {
if (str_contains($value, '--dirty')) {
$filters[] = new DirtyTestCaseFilter($rootPath);
}
}
$testSuite = TestSuite::getInstance(
$rootPath,
$argv->getParameterOption('--test-directory', (new ConfigLoader($rootPath))->getTestsDirectory())
$argv->getParameterOption('--test-directory', (new ConfigLoader($rootPath))->getTestsDirectory()),
new TestRepository($filters),
);
$isDecorated = $argv->getParameterOption('--colors', 'always') !== 'never';
@ -57,16 +68,10 @@ use Symfony\Component\Console\Output\OutputInterface;
$container->add(TestSuite::class, $testSuite);
$container->add(OutputInterface::class, $output);
$argsToUnset = ['--test-directory', '--compact', '--profile', '--dirty'];
foreach ($args as $key => $value) {
if (str_contains($value, '--test-directory')) {
unset($args[$key]);
}
if (str_contains($value, '--compact')) {
unset($args[$key]);
}
if (str_contains($value, '--profile')) {
if (in_array($value, $argsToUnset)) {
unset($args[$key]);
}
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Pest\Contracts;
interface TestCaseFilter
{
public function canLoad(string $suiteClassFile): bool;
}

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Pest\Repositories;
use Closure;
use Pest\Contracts\TestCaseFilter;
use Pest\Exceptions\TestCaseAlreadyInUse;
use Pest\Exceptions\TestCaseClassOrTraitNotFound;
use Pest\Factories\TestCaseFactory;
@ -27,6 +28,13 @@ final class TestRepository
*/
private array $uses = [];
/**
* @param array<int, TestCaseFilter> $testCaseFilters
*/
public function __construct(private readonly array $testCaseFilters = [])
{
}
/**
* Counts the number of test cases.
*/
@ -48,7 +56,7 @@ final class TestRepository
$testCases = $this->testCases;
}
return array_values(array_map(static fn (TestCaseFactory $factory): string => $factory->filename, $testCases));
return array_values(array_map(static fn(TestCaseFactory $factory): string => $factory->filename, $testCases));
}
/**
@ -97,7 +105,7 @@ final class TestRepository
*/
public function set(TestCaseMethodFactory $method): void
{
if (! array_key_exists($method->filename, $this->testCases)) {
if (!array_key_exists($method->filename, $this->testCases)) {
$this->testCases[$method->filename] = new TestCaseFactory($method->filename);
}
@ -109,7 +117,17 @@ final class TestRepository
*/
public function makeIfNeeded(string $filename): void
{
if (array_key_exists($filename, $this->testCases)) {
if (!array_key_exists($filename, $this->testCases)) {
return;
}
$canLoad = array_reduce(
$this->testCaseFilters,
fn(bool $carry, TestCaseFilter $filter): bool => $carry && $filter->canLoad($filename),
true,
);
if ($canLoad) {
$this->make($this->testCases[$filename]);
}
}
@ -119,12 +137,12 @@ final class TestRepository
*/
private function make(TestCaseFactory $testCase): void
{
$startsWith = static fn (string $target, string $directory): bool => Str::startsWith($target, $directory.DIRECTORY_SEPARATOR);
$startsWith = static fn(string $target, string $directory): bool => Str::startsWith($target, $directory . DIRECTORY_SEPARATOR);
foreach ($this->uses as $path => $uses) {
[$classOrTraits, $groups, $hooks] = $uses;
if ((! is_dir($path) && $testCase->filename === $path) || (is_dir($path) && $startsWith($testCase->filename, $path))) {
if ((!is_dir($path) && $testCase->filename === $path) || (is_dir($path) && $startsWith($testCase->filename, $path))) {
foreach ($classOrTraits as $class) {
/** @var string $class */
if (class_exists($class)) {

View File

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Pest\Support;
use Pest\Contracts\TestCaseFilter;
use Symfony\Component\Process\Process;
final class DirtyTestCaseFilter implements TestCaseFilter
{
/**
* @var array<string>
*/
private array $changedFiles = [];
public function __construct(private string $projectRoot)
{
$this->loadDiff();
}
public function canLoad(string $suiteClassFile): bool
{
$relativePath = str_replace($this->projectRoot, '', $suiteClassFile);
if (str_starts_with($relativePath, '/')) {
$relativePath = substr($relativePath, 1);
}
return in_array($relativePath, $this->changedFiles, true);
}
private function loadDiff(): void
{
$process = new Process([
'git',
'diff',
'--name-only',
'HEAD',
'--',
'*.php',
]);
$process->run();
$this->changedFiles = explode(PHP_EOL, trim($process->getOutput()));
}
}

View File

@ -23,11 +23,6 @@ final class TestSuite
*/
public ?TestCase $test = null;
/**
* Holds the tests repository.
*/
public TestRepository $tests;
/**
* Holds the before each repository.
*/
@ -68,11 +63,12 @@ final class TestSuite
*/
public function __construct(
string $rootPath,
public string $testPath)
public string $testPath,
public TestRepository $tests = new TestRepository(),
)
{
$this->beforeAll = new BeforeAllRepository();
$this->beforeEach = new BeforeEachRepository();
$this->tests = new TestRepository();
$this->afterEach = new AfterEachRepository();
$this->afterAll = new AfterAllRepository();
$this->retryRepository = new RetryRepository('retry');
@ -83,10 +79,14 @@ final class TestSuite
/**
* Returns the current instance of the test suite.
*/
public static function getInstance(string $rootPath = null, string $testPath = null): TestSuite
public static function getInstance(
string $rootPath = null,
string $testPath = null,
TestRepository $tests = null,
): TestSuite
{
if (is_string($rootPath) && is_string($testPath)) {
self::$instance = new TestSuite($rootPath, $testPath);
self::$instance = new TestSuite($rootPath, $testPath, $tests);
foreach (Plugin::$callables as $callable) {
$callable();