mirror of
https://github.com/pestphp/pest.git
synced 2026-04-21 06:27:28 +02:00
feat(tia): continues to work on poc
This commit is contained in:
@ -6,13 +6,16 @@ namespace Pest\Concerns;
|
|||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Pest\Exceptions\DatasetArgumentsMismatch;
|
use Pest\Exceptions\DatasetArgumentsMismatch;
|
||||||
|
use Pest\Contracts\Plugins\AfterEachable;
|
||||||
|
use Pest\Contracts\Plugins\BeforeEachable;
|
||||||
|
use Pest\Contracts\Plugins\Runnable;
|
||||||
use Pest\Panic;
|
use Pest\Panic;
|
||||||
|
use Pest\Plugin\Loader;
|
||||||
use Pest\Preset;
|
use Pest\Preset;
|
||||||
use Pest\Support\ChainableClosure;
|
use Pest\Support\ChainableClosure;
|
||||||
use Pest\Support\ExceptionTrace;
|
use Pest\Support\ExceptionTrace;
|
||||||
use Pest\Support\Reflection;
|
use Pest\Support\Reflection;
|
||||||
use Pest\Support\Shell;
|
use Pest\Support\Shell;
|
||||||
use Pest\Plugins\Tia\State as TiaState;
|
|
||||||
use Pest\TestSuite;
|
use Pest\TestSuite;
|
||||||
use PHPUnit\Framework\Attributes\PostCondition;
|
use PHPUnit\Framework\Attributes\PostCondition;
|
||||||
use PHPUnit\Framework\IncompleteTest;
|
use PHPUnit\Framework\IncompleteTest;
|
||||||
@ -228,15 +231,12 @@ trait Testable
|
|||||||
{
|
{
|
||||||
TestSuite::getInstance()->test = $this;
|
TestSuite::getInstance()->test = $this;
|
||||||
|
|
||||||
// TIA replay fast-path. When the file is known to the dependency graph
|
/** @var BeforeEachable $plugin */
|
||||||
// and none of its deps changed since recording, skip both the
|
foreach (Loader::getPlugins(BeforeEachable::class) as $plugin) {
|
||||||
// framework `setUp()` (Laravel app bootstrap, DB refresh, etc.) and
|
if ($plugin->beforeEach(self::$__filename, $this::class.'::'.$this->name()) === false) {
|
||||||
// the user `beforeEach` chain. The matching short-circuit inside
|
|
||||||
// `__runTest()` ensures the test body never executes, so no
|
|
||||||
// initialisation is needed.
|
|
||||||
if (TiaState::instance()->shouldReplayFromCache(self::$__filename, $this::class.'::'.$this->name())) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name());
|
$method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name());
|
||||||
|
|
||||||
@ -313,14 +313,14 @@ trait Testable
|
|||||||
*/
|
*/
|
||||||
protected function tearDown(...$arguments): void
|
protected function tearDown(...$arguments): void
|
||||||
{
|
{
|
||||||
// TIA replay: setUp was skipped, the closure never ran — there is
|
/** @var AfterEachable $plugin */
|
||||||
// no matching cleanup to perform here. Keep the framework invariant
|
foreach (Loader::getPlugins(AfterEachable::class) as $plugin) {
|
||||||
// of clearing the "current test" pointer and bail out.
|
if ($plugin->afterEach(self::$__filename, $this::class.'::'.$this->name()) === false) {
|
||||||
if (TiaState::instance()->shouldReplayFromCache(self::$__filename, $this::class.'::'.$this->name())) {
|
|
||||||
TestSuite::getInstance()->test = null;
|
TestSuite::getInstance()->test = null;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$afterEach = TestSuite::getInstance()->afterEach->get(self::$__filename);
|
$afterEach = TestSuite::getInstance()->afterEach->get(self::$__filename);
|
||||||
|
|
||||||
@ -347,14 +347,14 @@ trait Testable
|
|||||||
*/
|
*/
|
||||||
private function __runTest(Closure $closure, ...$args): mixed
|
private function __runTest(Closure $closure, ...$args): mixed
|
||||||
{
|
{
|
||||||
// TIA replay: the file's deps haven't changed and it last passed.
|
/** @var Runnable $plugin */
|
||||||
// Bypass the closure entirely and register a synthetic assertion so
|
foreach (Loader::getPlugins(Runnable::class) as $plugin) {
|
||||||
// PHPUnit does not emit a "risky: no assertions" warning.
|
if ($plugin->run(self::$__filename, $this::class.'::'.$this->name()) === false) {
|
||||||
if (TiaState::instance()->shouldReplayFromCache(self::$__filename, $this::class.'::'.$this->name())) {
|
|
||||||
$this->addToAssertionCount(1);
|
$this->addToAssertionCount(1);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$arguments = $this->__resolveTestArguments($args);
|
$arguments = $this->__resolveTestArguments($args);
|
||||||
$this->__ensureDatasetArgumentNameAndNumberMatches($arguments);
|
$this->__ensureDatasetArgumentNameAndNumberMatches($arguments);
|
||||||
|
|||||||
20
src/Contracts/Plugins/AfterEachable.php
Normal file
20
src/Contracts/Plugins/AfterEachable.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Pest\Contracts\Plugins;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called at the start of `tearDown`. Return `false` to skip the framework
|
||||||
|
* tearDown, afterEach chain, and method-level cleanup.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
interface AfterEachable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $filename Absolute path of the test file.
|
||||||
|
* @param string $testId Fully-qualified `Class::method` identifier.
|
||||||
|
*/
|
||||||
|
public function afterEach(string $filename, string $testId): bool;
|
||||||
|
}
|
||||||
26
src/Contracts/Plugins/BeforeEachable.php
Normal file
26
src/Contracts/Plugins/BeforeEachable.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Pest\Contracts\Plugins;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugins implementing this interface are called before each test's `setUp`.
|
||||||
|
*
|
||||||
|
* Return `false` to skip the test entirely — `setUp`, body and `tearDown`
|
||||||
|
* are all bypassed and the test counts as passed with one synthetic
|
||||||
|
* assertion. Any other return value lets the test proceed normally.
|
||||||
|
*
|
||||||
|
* Resolution happens once per process via `Loader::getPlugins`; the per-test
|
||||||
|
* call is a cheap iteration over the cached list.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
interface BeforeEachable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $filename Absolute path of the test file.
|
||||||
|
* @param string $testId Fully-qualified `Class::method` identifier.
|
||||||
|
*/
|
||||||
|
public function beforeEach(string $filename, string $testId): bool;
|
||||||
|
}
|
||||||
21
src/Contracts/Plugins/Runnable.php
Normal file
21
src/Contracts/Plugins/Runnable.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Pest\Contracts\Plugins;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called around the test body (`__runTest`). Return `false` to skip the
|
||||||
|
* closure — a synthetic assertion is registered so PHPUnit does not flag the
|
||||||
|
* test as risky.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
interface Runnable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $filename Absolute path of the test file.
|
||||||
|
* @param string $testId Fully-qualified `Class::method` identifier.
|
||||||
|
*/
|
||||||
|
public function run(string $filename, string $testId): bool;
|
||||||
|
}
|
||||||
@ -5,7 +5,10 @@ declare(strict_types=1);
|
|||||||
namespace Pest\Plugins;
|
namespace Pest\Plugins;
|
||||||
|
|
||||||
use Pest\Contracts\Plugins\AddsOutput;
|
use Pest\Contracts\Plugins\AddsOutput;
|
||||||
|
use Pest\Contracts\Plugins\AfterEachable;
|
||||||
|
use Pest\Contracts\Plugins\BeforeEachable;
|
||||||
use Pest\Contracts\Plugins\HandlesArguments;
|
use Pest\Contracts\Plugins\HandlesArguments;
|
||||||
|
use Pest\Contracts\Plugins\Runnable;
|
||||||
use Pest\Contracts\Plugins\Terminable;
|
use Pest\Contracts\Plugins\Terminable;
|
||||||
use Pest\Exceptions\NoDirtyTestsFound;
|
use Pest\Exceptions\NoDirtyTestsFound;
|
||||||
use Pest\Panic;
|
use Pest\Panic;
|
||||||
@ -62,7 +65,7 @@ use Throwable;
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
final class Tia implements AddsOutput, HandlesArguments, Terminable
|
final class Tia implements AddsOutput, AfterEachable, BeforeEachable, HandlesArguments, Runnable, Terminable
|
||||||
{
|
{
|
||||||
use Concerns\HandleArguments;
|
use Concerns\HandleArguments;
|
||||||
|
|
||||||
@ -130,6 +133,21 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable
|
|||||||
return $this->handleParent($arguments, $projectRoot, $forceRebuild);
|
return $this->handleParent($arguments, $projectRoot, $forceRebuild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function beforeEach(string $filename, string $testId): bool
|
||||||
|
{
|
||||||
|
return ! State::instance()->shouldReplayFromCache($filename, $testId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run(string $filename, string $testId): bool
|
||||||
|
{
|
||||||
|
return ! State::instance()->shouldReplayFromCache($filename, $testId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function afterEach(string $filename, string $testId): bool
|
||||||
|
{
|
||||||
|
return ! State::instance()->shouldReplayFromCache($filename, $testId);
|
||||||
|
}
|
||||||
|
|
||||||
public function terminate(): void
|
public function terminate(): void
|
||||||
{
|
{
|
||||||
if ($this->graphWritten) {
|
if ($this->graphWritten) {
|
||||||
|
|||||||
Reference in New Issue
Block a user