mirror of
https://github.com/pestphp/pest.git
synced 2026-03-10 17:57:23 +01:00
feat: basic PHPUnit 10 support
This commit is contained in:
@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use NunoMaduro\Collision\Adapters\Phpunit\Printer;
|
||||
use Pest\Logging\JUnit;
|
||||
use Pest\Logging\TeamCity;
|
||||
use PHPUnit\TextUI\DefaultResultPrinter;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class AddsDefaults
|
||||
{
|
||||
private const PRINTER = 'printer';
|
||||
|
||||
/**
|
||||
* Adds default arguments to the given `arguments` array.
|
||||
*
|
||||
* @param array<string, mixed> $arguments
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function to(array $arguments): array
|
||||
{
|
||||
if (!array_key_exists(self::PRINTER, $arguments)) {
|
||||
$arguments[self::PRINTER] = new Printer(null, $arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS);
|
||||
}
|
||||
|
||||
if ($arguments[self::PRINTER] === \PHPUnit\Util\Log\TeamCity::class) {
|
||||
$arguments[self::PRINTER] = new TeamCity(null, $arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS);
|
||||
}
|
||||
|
||||
// Load our junit logger instead.
|
||||
if (array_key_exists('junitLogfile', $arguments)) {
|
||||
$arguments['listeners'][] = new JUnit(
|
||||
$arguments['junitLogfile']
|
||||
);
|
||||
unset($arguments['junitLogfile']);
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use PHPUnit\Framework\WarningTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class AddsTests
|
||||
{
|
||||
/**
|
||||
* Adds tests to the given test suite.
|
||||
*
|
||||
* @param TestSuite<\PHPUnit\Framework\TestCase> $testSuite
|
||||
*/
|
||||
public static function to(TestSuite $testSuite, \Pest\TestSuite $pestTestSuite): void
|
||||
{
|
||||
self::removeTestClosureWarnings($testSuite);
|
||||
|
||||
$testSuites = [];
|
||||
$pestTestSuite->tests->build($pestTestSuite, function (TestCase $testCase) use (&$testSuites): void {
|
||||
$testCaseClass = get_class($testCase);
|
||||
if (!array_key_exists($testCaseClass, $testSuites)) {
|
||||
$testSuites[$testCaseClass] = [];
|
||||
}
|
||||
|
||||
$testSuites[$testCaseClass][] = $testCase;
|
||||
});
|
||||
|
||||
foreach ($testSuites as $testCaseName => $testCases) {
|
||||
$testTestSuite = new TestSuite($testCaseName);
|
||||
$testTestSuite->setTests([]);
|
||||
foreach ($testCases as $testCase) {
|
||||
$testTestSuite->addTest($testCase, $testCase->getGroups());
|
||||
}
|
||||
$testSuite->addTestSuite($testTestSuite);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TestSuite<\PHPUnit\Framework\TestCase> $testSuite
|
||||
*/
|
||||
private static function removeTestClosureWarnings(TestSuite $testSuite): void
|
||||
{
|
||||
$tests = $testSuite->tests();
|
||||
|
||||
foreach ($tests as $key => $test) {
|
||||
if ($test instanceof TestSuite) {
|
||||
self::removeTestClosureWarnings($test);
|
||||
}
|
||||
|
||||
if ($test instanceof WarningTestCase) {
|
||||
unset($tests[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$testSuite->setTests($tests);
|
||||
}
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use Pest\Exceptions\AttributeNotSupportedYet;
|
||||
use Pest\Exceptions\FileOrFolderNotFound;
|
||||
use PHPUnit\TextUI\XmlConfiguration\Loader;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ValidatesConfiguration
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const CONFIGURATION_KEY = 'configuration';
|
||||
|
||||
/**
|
||||
* Validates the configuration in the given `configuration`.
|
||||
*
|
||||
* @param array<string, mixed> $arguments
|
||||
*/
|
||||
public static function in($arguments): void
|
||||
{
|
||||
if (!array_key_exists(self::CONFIGURATION_KEY, $arguments) || !file_exists($arguments[self::CONFIGURATION_KEY])) {
|
||||
throw new FileOrFolderNotFound('phpunit.xml');
|
||||
}
|
||||
|
||||
$configuration = (new Loader())->load($arguments[self::CONFIGURATION_KEY])->phpunit();
|
||||
|
||||
if ($configuration->processIsolation()) {
|
||||
throw new AttributeNotSupportedYet('processIsolation', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
|
||||
use Pest\Exceptions\FileOrFolderNotFound;
|
||||
use Pest\TestSuite;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ValidatesEnvironment
|
||||
{
|
||||
/**
|
||||
* The need files on the root path.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const NEEDED_FILES = [
|
||||
'composer.json',
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the environment.
|
||||
*/
|
||||
public static function in(TestSuite $testSuite): void
|
||||
{
|
||||
$rootPath = $testSuite->rootPath;
|
||||
|
||||
$exists = function ($neededFile) use ($rootPath): bool {
|
||||
return file_exists(sprintf('%s%s%s', $rootPath, DIRECTORY_SEPARATOR, $neededFile));
|
||||
};
|
||||
|
||||
foreach (self::NEEDED_FILES as $neededFile) {
|
||||
if (!$exists($neededFile)) {
|
||||
throw new FileOrFolderNotFound($neededFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/Bootstrappers/BootEmitter.php
Normal file
29
src/Bootstrappers/BootEmitter.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use Pest\Emitters\DispatchingEmitter;
|
||||
use PHPUnit\Event;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BootEmitter
|
||||
{
|
||||
/**
|
||||
* Boots the Event Emitter.
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
if (!($baseEmitter = Event\Facade::emitter()) instanceof DispatchingEmitter) {
|
||||
$reflectedClass = new ReflectionClass(Event\Facade::class);
|
||||
|
||||
$reflectedClass->setStaticPropertyValue('emitter', new DispatchingEmitter(
|
||||
$baseEmitter,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/Bootstrappers/BootExceptionHandler.php
Normal file
21
src/Bootstrappers/BootExceptionHandler.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use NunoMaduro\Collision;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BootExceptionHandler
|
||||
{
|
||||
/**
|
||||
* Boots the Exception Handler.
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
(new Collision\Provider())->register();
|
||||
}
|
||||
}
|
||||
@ -2,18 +2,18 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Actions;
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use Pest\Support\Str;
|
||||
use function Pest\testDirectory;
|
||||
use PHPUnit\Util\FileLoader;
|
||||
use Pest\TestSuite;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class LoadStructure
|
||||
final class BootFiles
|
||||
{
|
||||
/**
|
||||
* The Pest convention.
|
||||
@ -21,23 +21,23 @@ final class LoadStructure
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const STRUCTURE = [
|
||||
'Expectations.php',
|
||||
'Datasets',
|
||||
'Datasets.php',
|
||||
'Expectations',
|
||||
'Expectations.php',
|
||||
'Helpers',
|
||||
'Helpers.php',
|
||||
'Pest.php',
|
||||
'Datasets',
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates the configuration in the given `configuration`.
|
||||
* Boots the Subscribers.
|
||||
*/
|
||||
public static function in(string $rootPath): void
|
||||
public function __invoke(): void
|
||||
{
|
||||
$testsPath = $rootPath . DIRECTORY_SEPARATOR . testDirectory();
|
||||
$rootPath = TestSuite::getInstance()->rootPath;
|
||||
|
||||
$load = function ($filename): bool {
|
||||
return file_exists($filename) && (bool) FileLoader::checkAndLoad($filename);
|
||||
};
|
||||
$testsPath = $rootPath . DIRECTORY_SEPARATOR . testDirectory();
|
||||
|
||||
foreach (self::STRUCTURE as $filename) {
|
||||
$filename = sprintf('%s%s%s', $testsPath, DIRECTORY_SEPARATOR, $filename);
|
||||
@ -50,14 +50,21 @@ final class LoadStructure
|
||||
$directory = new RecursiveDirectoryIterator($filename);
|
||||
$iterator = new RecursiveIteratorIterator($directory);
|
||||
foreach ($iterator as $file) {
|
||||
$filename = $file->__toString();
|
||||
if (Str::endsWith($filename, '.php') && file_exists($filename)) {
|
||||
require_once $filename;
|
||||
}
|
||||
$this->load($file->__toString());
|
||||
}
|
||||
} else {
|
||||
$load($filename);
|
||||
$this->load($filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given filename, if possible.
|
||||
*/
|
||||
private function load(string $filename): void
|
||||
{
|
||||
if (Str::endsWith($filename, '.php') && file_exists($filename)) {
|
||||
include_once $filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/Bootstrappers/BootSubscribers.php
Normal file
37
src/Bootstrappers/BootSubscribers.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Bootstrappers;
|
||||
|
||||
use Pest\Subscribers;
|
||||
use PHPUnit\Event;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BootSubscribers
|
||||
{
|
||||
/**
|
||||
* The Kernel subscribers.
|
||||
*
|
||||
* @var array<int, class-string>
|
||||
*/
|
||||
private static array $subscribers = [
|
||||
Subscribers\EnsureTestsAreLoaded::class,
|
||||
Subscribers\EnsureConfigurationIsValid::class,
|
||||
Subscribers\EnsureConfigurationDefaults::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Boots the Subscribers.
|
||||
*/
|
||||
public function __invoke(): void
|
||||
{
|
||||
foreach (self::$subscribers as $subscriber) {
|
||||
Event\Facade::registerSubscriber(
|
||||
new $subscriber()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Pest\Concerns;
|
||||
|
||||
use Closure;
|
||||
use Pest\Support\Backtrace;
|
||||
use Pest\Support\ChainableClosure;
|
||||
use Pest\Support\ExceptionTrace;
|
||||
use Pest\TestSuite;
|
||||
@ -12,8 +13,7 @@ use PHPUnit\Framework\ExecutionOrderDependency;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* To avoid inheritance conflicts, all the fields related
|
||||
* to Pest only will be prefixed by double underscore.
|
||||
* To avoid inheritance conflicts, all the fields related to Pest only will be prefixed by double underscore.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
@ -40,7 +40,7 @@ trait Testable
|
||||
*
|
||||
* @var Closure|null
|
||||
*/
|
||||
private $beforeEach = null;
|
||||
private $__beforeEach = null;
|
||||
|
||||
/**
|
||||
* Holds a global/shared afterEach ("tear down") closure if one has been
|
||||
@ -48,7 +48,7 @@ trait Testable
|
||||
*
|
||||
* @var Closure|null
|
||||
*/
|
||||
private $afterEach = null;
|
||||
private $__afterEach = null;
|
||||
|
||||
/**
|
||||
* Holds a global/shared beforeAll ("set up before") closure if one has been
|
||||
@ -56,7 +56,7 @@ trait Testable
|
||||
*
|
||||
* @var Closure|null
|
||||
*/
|
||||
private static $beforeAll = null;
|
||||
private static $__beforeAll = null;
|
||||
|
||||
/**
|
||||
* Holds a global/shared afterAll ("tear down after") closure if one has
|
||||
@ -64,19 +64,21 @@ trait Testable
|
||||
*
|
||||
* @var Closure|null
|
||||
*/
|
||||
private static $afterAll = null;
|
||||
private static $__afterAll = null;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the test case.
|
||||
*/
|
||||
public function __construct(Closure $test, string $description, array $data)
|
||||
{
|
||||
$this->__test = $test;
|
||||
$this->__description = $description;
|
||||
self::$beforeAll = null;
|
||||
self::$afterAll = null;
|
||||
$this->__test = $test;
|
||||
$this->__description = $description;
|
||||
self::$__beforeAll = null;
|
||||
self::$__afterAll = null;
|
||||
|
||||
parent::__construct('__test', $data);
|
||||
parent::__construct('__test');
|
||||
|
||||
$this->setData($description, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,7 +86,7 @@ trait Testable
|
||||
*/
|
||||
public function addGroups(array $groups): void
|
||||
{
|
||||
$groups = array_unique(array_merge($this->getGroups(), $groups));
|
||||
$groups = array_unique(array_merge($this->groups(), $groups));
|
||||
|
||||
$this->setGroups($groups);
|
||||
}
|
||||
@ -101,7 +103,7 @@ trait Testable
|
||||
$test = "{$className}::{$test}";
|
||||
}
|
||||
|
||||
return new ExecutionOrderDependency($test, null, '');
|
||||
return new ExecutionOrderDependency($test, '__test');
|
||||
}, $tests);
|
||||
|
||||
$this->setDependencies($tests);
|
||||
@ -111,14 +113,14 @@ trait Testable
|
||||
* Add a shared/"global" before all test hook that will execute **before**
|
||||
* the test defined `beforeAll` hook(s).
|
||||
*/
|
||||
public function addBeforeAll(?Closure $hook): void
|
||||
public function __addBeforeAll(?Closure $hook): void
|
||||
{
|
||||
if (!$hook) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$beforeAll = (self::$beforeAll instanceof Closure)
|
||||
? ChainableClosure::fromStatic(self::$beforeAll, $hook)
|
||||
self::$__beforeAll = (self::$__beforeAll instanceof Closure)
|
||||
? ChainableClosure::fromStatic(self::$__beforeAll, $hook)
|
||||
: $hook;
|
||||
}
|
||||
|
||||
@ -126,14 +128,14 @@ trait Testable
|
||||
* Add a shared/"global" after all test hook that will execute **before**
|
||||
* the test defined `afterAll` hook(s).
|
||||
*/
|
||||
public function addAfterAll(?Closure $hook): void
|
||||
public function __addAfterAll(?Closure $hook): void
|
||||
{
|
||||
if (!$hook) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$afterAll = (self::$afterAll instanceof Closure)
|
||||
? ChainableClosure::fromStatic(self::$afterAll, $hook)
|
||||
self::$__afterAll = (self::$__afterAll instanceof Closure)
|
||||
? ChainableClosure::fromStatic(self::$__afterAll, $hook)
|
||||
: $hook;
|
||||
}
|
||||
|
||||
@ -141,24 +143,24 @@ trait Testable
|
||||
* Add a shared/"global" before each test hook that will execute **before**
|
||||
* the test defined `beforeEach` hook.
|
||||
*/
|
||||
public function addBeforeEach(?Closure $hook): void
|
||||
public function __addBeforeEach(?Closure $hook): void
|
||||
{
|
||||
$this->addHook('beforeEach', $hook);
|
||||
$this->__addHook('__beforeEach', $hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a shared/"global" after each test hook that will execute **before**
|
||||
* the test defined `afterEach` hook.
|
||||
*/
|
||||
public function addAfterEach(?Closure $hook): void
|
||||
public function __addAfterEach(?Closure $hook): void
|
||||
{
|
||||
$this->addHook('afterEach', $hook);
|
||||
$this->__addHook('__afterEach', $hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a shared/global hook and compose them if more than one is passed.
|
||||
*/
|
||||
private function addHook(string $property, ?Closure $hook): void
|
||||
private function __addHook(string $property, ?Closure $hook): void
|
||||
{
|
||||
if (!$hook) {
|
||||
return;
|
||||
@ -176,7 +178,9 @@ trait Testable
|
||||
*/
|
||||
public function getName(bool $withDataSet = true): string
|
||||
{
|
||||
return $this->__description;
|
||||
return (str_ends_with(Backtrace::file(), 'TestRunner.php') || Backtrace::line() === 277)
|
||||
? '__test'
|
||||
: $this->__description;
|
||||
}
|
||||
|
||||
public static function __getFileName(): string
|
||||
@ -193,8 +197,8 @@ trait Testable
|
||||
|
||||
$beforeAll = TestSuite::getInstance()->beforeAll->get(self::$__filename);
|
||||
|
||||
if (self::$beforeAll instanceof Closure) {
|
||||
$beforeAll = ChainableClosure::fromStatic(self::$beforeAll, $beforeAll);
|
||||
if (self::$__beforeAll instanceof Closure) {
|
||||
$beforeAll = ChainableClosure::fromStatic(self::$__beforeAll, $beforeAll);
|
||||
}
|
||||
|
||||
call_user_func(Closure::bind($beforeAll, null, self::class));
|
||||
@ -207,8 +211,8 @@ trait Testable
|
||||
{
|
||||
$afterAll = TestSuite::getInstance()->afterAll->get(self::$__filename);
|
||||
|
||||
if (self::$afterAll instanceof Closure) {
|
||||
$afterAll = ChainableClosure::fromStatic(self::$afterAll, $afterAll);
|
||||
if (self::$__afterAll instanceof Closure) {
|
||||
$afterAll = ChainableClosure::fromStatic(self::$__afterAll, $afterAll);
|
||||
}
|
||||
|
||||
call_user_func(Closure::bind($afterAll, null, self::class));
|
||||
@ -227,8 +231,8 @@ trait Testable
|
||||
|
||||
$beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename);
|
||||
|
||||
if ($this->beforeEach instanceof Closure) {
|
||||
$beforeEach = ChainableClosure::from($this->beforeEach, $beforeEach);
|
||||
if ($this->__beforeEach instanceof Closure) {
|
||||
$beforeEach = ChainableClosure::from($this->__beforeEach, $beforeEach);
|
||||
}
|
||||
|
||||
$this->__callClosure($beforeEach, func_get_args());
|
||||
@ -241,8 +245,8 @@ trait Testable
|
||||
{
|
||||
$afterEach = TestSuite::getInstance()->afterEach->get(self::$__filename);
|
||||
|
||||
if ($this->afterEach instanceof Closure) {
|
||||
$afterEach = ChainableClosure::from($this->afterEach, $afterEach);
|
||||
if ($this->__afterEach instanceof Closure) {
|
||||
$afterEach = ChainableClosure::from($this->__afterEach, $afterEach);
|
||||
}
|
||||
|
||||
$this->__callClosure($afterEach, func_get_args());
|
||||
@ -273,7 +277,7 @@ trait Testable
|
||||
*/
|
||||
public function __test()
|
||||
{
|
||||
return $this->__callClosure($this->__test, $this->resolveTestArguments(func_get_args()));
|
||||
return $this->__callClosure($this->__test, $this->__resolveTestArguments(func_get_args()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -281,7 +285,7 @@ trait Testable
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
private function resolveTestArguments(array $arguments): array
|
||||
private function __resolveTestArguments(array $arguments): array
|
||||
{
|
||||
return array_map(function ($data) {
|
||||
return $data instanceof Closure ? $this->__callClosure($data, []) : $data;
|
||||
|
||||
@ -1,132 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Console;
|
||||
|
||||
use Pest\Actions\AddsDefaults;
|
||||
use Pest\Actions\AddsTests;
|
||||
use Pest\Actions\InteractsWithPlugins;
|
||||
use Pest\Actions\LoadStructure;
|
||||
use Pest\Actions\ValidatesConfiguration;
|
||||
use Pest\Plugins\Version;
|
||||
use Pest\Support\Container;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Framework\TestSuite as BaseTestSuite;
|
||||
use PHPUnit\TextUI\Command as BaseCommand;
|
||||
use PHPUnit\TextUI\TestRunner;
|
||||
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Command extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* Holds the current testing suite.
|
||||
*
|
||||
* @var TestSuite
|
||||
*/
|
||||
private $testSuite;
|
||||
|
||||
/**
|
||||
* Holds the current console output.
|
||||
*
|
||||
* @var OutputInterface
|
||||
*/
|
||||
private $output;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the command class.
|
||||
*/
|
||||
public function __construct(TestSuite $testSuite, OutputInterface $output)
|
||||
{
|
||||
$this->testSuite = $testSuite;
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
*
|
||||
* @param array<int, string> $argv
|
||||
*/
|
||||
protected function handleArguments(array $argv): void
|
||||
{
|
||||
$argv = InteractsWithPlugins::handleArguments($argv);
|
||||
|
||||
parent::handleArguments($argv);
|
||||
|
||||
/*
|
||||
* Let's validate the configuration. Making
|
||||
* sure all options are yet supported by Pest.
|
||||
*/
|
||||
ValidatesConfiguration::in($this->arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PHPUnit test runner.
|
||||
*/
|
||||
protected function createRunner(): TestRunner
|
||||
{
|
||||
/*
|
||||
* First, let's add the defaults we use on `pest`. Those
|
||||
* are the printer class, and others that may be appear.
|
||||
*/
|
||||
$this->arguments = AddsDefaults::to($this->arguments);
|
||||
|
||||
$testRunner = new TestRunner($this->arguments['loader']);
|
||||
$testSuite = $this->arguments['test'];
|
||||
|
||||
if (is_string($testSuite)) {
|
||||
if (\is_dir($testSuite)) {
|
||||
/** @var string[] $files */
|
||||
$files = (new FileIteratorFacade())->getFilesAsArray(
|
||||
$testSuite,
|
||||
$this->arguments['testSuffixes']
|
||||
);
|
||||
} else {
|
||||
$files = [$testSuite];
|
||||
}
|
||||
|
||||
$testSuite = new BaseTestSuite($testSuite);
|
||||
|
||||
$testSuite->addTestFiles($files);
|
||||
|
||||
$this->arguments['test'] = $testSuite;
|
||||
}
|
||||
|
||||
AddsTests::to($testSuite, $this->testSuite);
|
||||
|
||||
return $testRunner;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
*
|
||||
* @param array<int, string> $argv
|
||||
*/
|
||||
public function run(array $argv, bool $exit = true): int
|
||||
{
|
||||
LoadStructure::in($this->testSuite->rootPath);
|
||||
|
||||
$result = parent::run($argv, false);
|
||||
$result = InteractsWithPlugins::addOutput($result);
|
||||
|
||||
exit($result);
|
||||
}
|
||||
|
||||
protected function showHelp(): void
|
||||
{
|
||||
/** @var Version $version */
|
||||
$version = Container::getInstance()->get(Version::class);
|
||||
$version->handleArguments(['--version']);
|
||||
parent::showHelp();
|
||||
|
||||
(new Help($this->output))();
|
||||
}
|
||||
}
|
||||
72
src/Console/Kernel.php
Normal file
72
src/Console/Kernel.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Console;
|
||||
|
||||
use Pest\Actions\InteractsWithPlugins;
|
||||
use Pest\Bootstrappers;
|
||||
use PHPUnit\TextUI\Application;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Kernel
|
||||
{
|
||||
/**
|
||||
* The Kernel bootstrappers.
|
||||
*
|
||||
* @var array<int, class-string>
|
||||
*/
|
||||
private static array $bootstrappers = [
|
||||
Bootstrappers\BootExceptionHandler::class,
|
||||
Bootstrappers\BootEmitter::class,
|
||||
Bootstrappers\BootSubscribers::class,
|
||||
Bootstrappers\BootFiles::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates a new Kernel instance.
|
||||
*/
|
||||
public function __construct(
|
||||
private Application $application
|
||||
) {
|
||||
// ..
|
||||
}
|
||||
|
||||
/**
|
||||
* Boots the Kernel.
|
||||
*/
|
||||
public static function boot(): self
|
||||
{
|
||||
foreach (self::$bootstrappers as $bootstrapper) {
|
||||
(new $bootstrapper())->__invoke();
|
||||
}
|
||||
|
||||
return new self(new Application());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the given argv.
|
||||
*
|
||||
* @param array<int, string> $argv
|
||||
*/
|
||||
public function handle(array $argv): int
|
||||
{
|
||||
$argv = InteractsWithPlugins::handleArguments($argv);
|
||||
|
||||
$result = $this->application->run(
|
||||
$argv, false,
|
||||
);
|
||||
|
||||
return InteractsWithPlugins::addOutput($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the Kernel.
|
||||
*/
|
||||
public function shutdown(): void
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
248
src/Emitters/DispatchingEmitter.php
Normal file
248
src/Emitters/DispatchingEmitter.php
Normal file
@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Emitters;
|
||||
|
||||
use Pest\Subscribers\EnsureTestsAreLoaded;
|
||||
use PHPUnit\Event\Code;
|
||||
use PHPUnit\Event\Code\Throwable;
|
||||
use PHPUnit\Event\Emitter;
|
||||
use PHPUnit\Framework\Constraint;
|
||||
use PHPUnit\Framework\TestResult;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use PHPUnit\TextUI\Configuration\Configuration;
|
||||
use SebastianBergmann\GlobalState\Snapshot;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class DispatchingEmitter implements Emitter
|
||||
{
|
||||
public function __construct(private Emitter $baseEmitter)
|
||||
{
|
||||
// ..
|
||||
}
|
||||
|
||||
public function eventFacadeSealed(): void
|
||||
{
|
||||
$this->baseEmitter->eventFacadeSealed(...func_get_args());
|
||||
}
|
||||
|
||||
public function testRunnerStarted(): void
|
||||
{
|
||||
$this->baseEmitter->testRunnerStarted(...func_get_args());
|
||||
}
|
||||
|
||||
public function testRunnerConfigured(Configuration $configuration): void
|
||||
{
|
||||
$this->baseEmitter->testRunnerConfigured($configuration);
|
||||
}
|
||||
|
||||
public function testRunnerFinished(): void
|
||||
{
|
||||
$this->baseEmitter->testRunnerFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function assertionMade(mixed $value, Constraint\Constraint $constraint, string $message, bool $hasFailed): void
|
||||
{
|
||||
$this->baseEmitter->assertionMade($value, $constraint, $message, $hasFailed);
|
||||
}
|
||||
|
||||
public function bootstrapFinished(string $filename): void
|
||||
{
|
||||
$this->baseEmitter->bootstrapFinished($filename);
|
||||
}
|
||||
|
||||
public function comparatorRegistered(string $className): void
|
||||
{
|
||||
$this->baseEmitter->comparatorRegistered($className);
|
||||
}
|
||||
|
||||
public function extensionLoaded(string $name, string $version): void
|
||||
{
|
||||
$this->baseEmitter->extensionLoaded($name, $version);
|
||||
}
|
||||
|
||||
public function globalStateCaptured(Snapshot $snapshot): void
|
||||
{
|
||||
$this->baseEmitter->globalStateCaptured($snapshot);
|
||||
}
|
||||
|
||||
public function globalStateModified(Snapshot $snapshotBefore, Snapshot $snapshotAfter, string $diff): void
|
||||
{
|
||||
$this->baseEmitter->globalStateModified($snapshotBefore, $snapshotAfter, $diff);
|
||||
}
|
||||
|
||||
public function globalStateRestored(Snapshot $snapshot): void
|
||||
{
|
||||
$this->baseEmitter->globalStateRestored($snapshot);
|
||||
}
|
||||
|
||||
public function testErrored(Code\Test $test, Throwable $throwable): void
|
||||
{
|
||||
$this->baseEmitter->testErrored(...func_get_args());
|
||||
}
|
||||
|
||||
public function testFailed(Code\Test $test, Throwable $throwable): void
|
||||
{
|
||||
$this->baseEmitter->testFailed(...func_get_args());
|
||||
}
|
||||
|
||||
public function testFinished(Code\Test $test): void
|
||||
{
|
||||
$this->baseEmitter->testFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testOutputPrinted(Code\Test $test, string $output): void
|
||||
{
|
||||
$this->baseEmitter->testOutputPrinted(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPassed(Code\Test $test): void
|
||||
{
|
||||
$this->baseEmitter->testPassed(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPassedWithWarning(Code\Test $test, Throwable $throwable): void
|
||||
{
|
||||
$this->baseEmitter->testPassedWithWarning(...func_get_args());
|
||||
}
|
||||
|
||||
public function testConsideredRisky(Code\Test $test, Throwable $throwable): void
|
||||
{
|
||||
$this->baseEmitter->testConsideredRisky(...func_get_args());
|
||||
}
|
||||
|
||||
public function testAborted(Code\Test $test, Throwable $throwable): void
|
||||
{
|
||||
$this->baseEmitter->testAborted(...func_get_args());
|
||||
}
|
||||
|
||||
public function testSkipped(Code\Test $test, string $message): void
|
||||
{
|
||||
$this->baseEmitter->testSkipped(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPrepared(Code\Test $test): void
|
||||
{
|
||||
$this->baseEmitter->testPrepared(...func_get_args());
|
||||
}
|
||||
|
||||
public function testAfterTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
|
||||
{
|
||||
$this->baseEmitter->testAfterTestMethodFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testAfterLastTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
|
||||
{
|
||||
$this->baseEmitter->testAfterLastTestMethodFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testBeforeFirstTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void
|
||||
{
|
||||
$this->baseEmitter->testBeforeFirstTestMethodCalled(...func_get_args());
|
||||
}
|
||||
|
||||
public function testBeforeFirstTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
|
||||
{
|
||||
$this->baseEmitter->testBeforeFirstTestMethodFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testBeforeTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void
|
||||
{
|
||||
$this->baseEmitter->testBeforeTestMethodCalled(...func_get_args());
|
||||
}
|
||||
|
||||
public function testBeforeTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
|
||||
{
|
||||
$this->baseEmitter->testBeforeTestMethodFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPreConditionCalled(string $testClassName, Code\ClassMethod $calledMethod): void
|
||||
{
|
||||
$this->baseEmitter->testPreConditionCalled(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPreConditionFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
|
||||
{
|
||||
$this->baseEmitter->testPreConditionFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPostConditionCalled(string $testClassName, Code\ClassMethod $calledMethod): void
|
||||
{
|
||||
$this->baseEmitter->testPostConditionCalled(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPostConditionFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
|
||||
{
|
||||
$this->baseEmitter->testPostConditionFinished(...func_get_args());
|
||||
}
|
||||
|
||||
public function testAfterTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void
|
||||
{
|
||||
$this->baseEmitter->testAfterTestMethodCalled(...func_get_args());
|
||||
}
|
||||
|
||||
public function testAfterLastTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void
|
||||
{
|
||||
$this->baseEmitter->testAfterLastTestMethodCalled(...func_get_args());
|
||||
}
|
||||
|
||||
public function testMockObjectCreated(string $className): void
|
||||
{
|
||||
$this->baseEmitter->testMockObjectCreated(...func_get_args());
|
||||
}
|
||||
|
||||
public function testMockObjectCreatedForTrait(string $traitName): void
|
||||
{
|
||||
$this->baseEmitter->testMockObjectCreatedForTrait(...func_get_args());
|
||||
}
|
||||
|
||||
public function testMockObjectCreatedForAbstractClass(string $className): void
|
||||
{
|
||||
$this->baseEmitter->testMockObjectCreatedForAbstractClass(...func_get_args());
|
||||
}
|
||||
|
||||
public function testMockObjectCreatedFromWsdl(string $wsdlFile, string $originalClassName, string $mockClassName, array $methods, bool $callOriginalConstructor, array $options): void
|
||||
{
|
||||
$this->baseEmitter->testMockObjectCreatedFromWsdl(...func_get_args());
|
||||
}
|
||||
|
||||
public function testPartialMockObjectCreated(string $className, string ...$methodNames): void
|
||||
{
|
||||
$this->baseEmitter->testPartialMockObjectCreated(...func_get_args());
|
||||
}
|
||||
|
||||
public function testTestProxyCreated(string $className, array $constructorArguments): void
|
||||
{
|
||||
$this->baseEmitter->testTestProxyCreated(...func_get_args());
|
||||
}
|
||||
|
||||
public function testTestStubCreated(string $className): void
|
||||
{
|
||||
$this->baseEmitter->testTestStubCreated(...func_get_args());
|
||||
}
|
||||
|
||||
public function testSuiteLoaded(TestSuite $testSuite): void
|
||||
{
|
||||
EnsureTestsAreLoaded::setTestSuite($testSuite);
|
||||
|
||||
$this->baseEmitter->testSuiteLoaded(...func_get_args());
|
||||
}
|
||||
|
||||
public function testSuiteSorted(int $executionOrder, int $executionOrderDefects, bool $resolveDependencies): void
|
||||
{
|
||||
$this->baseEmitter->testSuiteSorted(...func_get_args());
|
||||
}
|
||||
|
||||
public function testSuiteStarted(TestSuite $testSuite): void
|
||||
{
|
||||
$this->baseEmitter->testSuiteStarted(...func_get_args());
|
||||
}
|
||||
|
||||
public function testSuiteFinished(TestSuite $testSuite, TestResult $result): void
|
||||
{
|
||||
$this->baseEmitter->testSuiteFinished(...func_get_args());
|
||||
}
|
||||
}
|
||||
@ -92,16 +92,14 @@ final class TestCaseFactory
|
||||
public $factoryProxies;
|
||||
|
||||
/**
|
||||
* Holds the higher order
|
||||
* messages that are proxyble.
|
||||
* Holds the higher order messages that are proxyble.
|
||||
*
|
||||
* @var HigherOrderMessageCollection
|
||||
*/
|
||||
public $proxies;
|
||||
|
||||
/**
|
||||
* Holds the higher order
|
||||
* messages that are chainable.
|
||||
* Holds the higher order messages that are chainable.
|
||||
*
|
||||
* @var HigherOrderMessageCollection
|
||||
*/
|
||||
@ -232,7 +230,7 @@ final class TestCaseFactory
|
||||
/**
|
||||
* Determine if the test case will receive argument input from Pest, or not.
|
||||
*/
|
||||
public function receivesArguments(): bool
|
||||
public function __receivesArguments(): bool
|
||||
{
|
||||
return count($this->datasets) > 0
|
||||
|| $this->factoryProxies->count('addDependencies') > 0;
|
||||
|
||||
@ -41,6 +41,7 @@ final class AfterEachRepository
|
||||
|
||||
return ChainableClosure::from(function (): void {
|
||||
if (class_exists(Mockery::class)) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
if ($container = Mockery::getContainer()) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
$this->addToAssertionCount($container->mockery_getExpectationCount());
|
||||
|
||||
@ -86,12 +86,11 @@ final class TestRepository
|
||||
}
|
||||
}
|
||||
|
||||
// IDEA: Consider set the real lines on these.
|
||||
$testCase->factoryProxies->add($filename, 0, 'addGroups', [$groups]);
|
||||
$testCase->factoryProxies->add($filename, 0, 'addBeforeAll', [$hooks[0] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, 'addBeforeEach', [$hooks[1] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, 'addAfterEach', [$hooks[2] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, 'addAfterAll', [$hooks[3] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, '__addBeforeAll', [$hooks[0] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, '__addBeforeEach', [$hooks[1] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, '__addAfterEach', [$hooks[2] ?? null]);
|
||||
$testCase->factoryProxies->add($filename, 0, '__addAfterAll', [$hooks[3] ?? null]);
|
||||
}
|
||||
};
|
||||
|
||||
@ -171,7 +170,7 @@ final class TestRepository
|
||||
throw new TestAlreadyExist($test->filename, $test->description);
|
||||
}
|
||||
|
||||
if (!$test->receivesArguments()) {
|
||||
if (!$test->__receivesArguments()) {
|
||||
$arguments = Reflection::getFunctionArguments($test->test);
|
||||
|
||||
if (count($arguments) > 0) {
|
||||
|
||||
22
src/Subscribers/EnsureConfigurationDefaults.php
Normal file
22
src/Subscribers/EnsureConfigurationDefaults.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Subscribers;
|
||||
|
||||
use PHPUnit\Event\TestRunner\Configured;
|
||||
use PHPUnit\Event\TestRunner\ConfiguredSubscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class EnsureConfigurationDefaults implements ConfiguredSubscriber
|
||||
{
|
||||
/**
|
||||
* Runs the subscriber.
|
||||
*/
|
||||
public function notify(Configured $event): void
|
||||
{
|
||||
$configuration = $event->configuration();
|
||||
}
|
||||
}
|
||||
27
src/Subscribers/EnsureConfigurationIsValid.php
Normal file
27
src/Subscribers/EnsureConfigurationIsValid.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Subscribers;
|
||||
|
||||
use Pest\Exceptions\AttributeNotSupportedYet;
|
||||
use PHPUnit\Event\TestRunner\Configured;
|
||||
use PHPUnit\Event\TestRunner\ConfiguredSubscriber;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class EnsureConfigurationIsValid implements ConfiguredSubscriber
|
||||
{
|
||||
/**
|
||||
* Runs the subscriber.
|
||||
*/
|
||||
public function notify(Configured $event): void
|
||||
{
|
||||
$configuration = $event->configuration();
|
||||
|
||||
if ($configuration->processIsolation()) {
|
||||
throw new AttributeNotSupportedYet('processIsolation', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
79
src/Subscribers/EnsureTestsAreLoaded.php
Normal file
79
src/Subscribers/EnsureTestsAreLoaded.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Subscribers;
|
||||
|
||||
use PHPUnit\Event\TestSuite\Loaded;
|
||||
use PHPUnit\Event\TestSuite\LoadedSubscriber;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PHPUnit\Framework\TestSuite;
|
||||
use PHPUnit\Framework\WarningTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class EnsureTestsAreLoaded implements LoadedSubscriber
|
||||
{
|
||||
/**
|
||||
* The current test suite, if any.
|
||||
*/
|
||||
private static ?TestSuite $testSuite;
|
||||
|
||||
/**
|
||||
* Runs the subscriber.
|
||||
*/
|
||||
public function notify(Loaded $event): void
|
||||
{
|
||||
$this->removeWarnings(self::$testSuite);
|
||||
|
||||
$testSuites = [];
|
||||
|
||||
$testSuite = \Pest\TestSuite::getInstance();
|
||||
$testSuite->tests->build($testSuite, function (TestCase $testCase) use (&$testSuites): void {
|
||||
$testCaseClass = get_class($testCase);
|
||||
if (!array_key_exists($testCaseClass, $testSuites)) {
|
||||
$testSuites[$testCaseClass] = [];
|
||||
}
|
||||
|
||||
$testSuites[$testCaseClass][] = $testCase;
|
||||
});
|
||||
|
||||
foreach ($testSuites as $testCaseName => $testCases) {
|
||||
$testTestSuite = new TestSuite($testCaseName);
|
||||
$testTestSuite->setTests([]);
|
||||
foreach ($testCases as $testCase) {
|
||||
$testTestSuite->addTest($testCase, $testCase->groups());
|
||||
}
|
||||
self::$testSuite->addTestSuite($testTestSuite);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current test suite.
|
||||
*/
|
||||
public static function setTestSuite(TestSuite $testSuite): void
|
||||
{
|
||||
self::$testSuite = $testSuite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the test case that have "empty" warnings.
|
||||
*/
|
||||
private function removeWarnings(TestSuite $testSuite): void
|
||||
{
|
||||
$tests = $testSuite->tests();
|
||||
|
||||
foreach ($tests as $key => $test) {
|
||||
if ($test instanceof TestSuite) {
|
||||
$this->removeWarnings($test);
|
||||
}
|
||||
|
||||
if ($test instanceof WarningTestCase) {
|
||||
unset($tests[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$testSuite->setTests(array_values($tests));
|
||||
}
|
||||
}
|
||||
@ -26,7 +26,7 @@ final class Backtrace
|
||||
$current = null;
|
||||
|
||||
foreach (debug_backtrace(self::BACKTRACE_OPTIONS) as $trace) {
|
||||
if (Str::endsWith($trace[self::FILE], (string) realpath('vendor/phpunit/phpunit/src/Util/FileLoader.php'))) {
|
||||
if (Str::endsWith($trace[self::FILE], (string) realpath('overrides/Runner/TestSuiteLoader.php'))) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user