Merge pull request #662 from pestphp/2.x_parallel_cleanup

2.x Parallel todo support
This commit is contained in:
Nuno Maduro
2023-02-13 13:24:54 +00:00
committed by GitHub
12 changed files with 124 additions and 62 deletions

View File

@ -1 +0,0 @@
["P\\Tests\\Temporary\\A::it_can_run_a_test_5#(3)","P\\Tests\\Temporary\\A::it_can_run_a_test_5#(4)"]

File diff suppressed because one or more lines are too long

View File

@ -7,7 +7,6 @@ use ParaTest\WrapperRunner\WrapperWorker;
use Pest\ConfigLoader;
use Pest\Kernel;
use Pest\Plugins\Actions\CallsHandleArguments;
use Pest\TestCaseMethodFilters\TodoTestCaseFilter;
use Pest\TestSuite;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
@ -15,7 +14,6 @@ use Symfony\Component\Console\Output\OutputInterface;
$bootPest = (static function (): void {
$workerArgv = new ArgvInput();
$masterArgv = new ArgvInput(json_decode($_SERVER['PEST_PARALLEL_ARGV']));
$rootPath = dirname(PHPUNIT_COMPOSER_INSTALL, 2);
$testSuite = TestSuite::getInstance($rootPath, $workerArgv->getParameterOption(
@ -23,10 +21,6 @@ $bootPest = (static function (): void {
(new ConfigLoader($rootPath))->getTestsDirectory()
));
if ($masterArgv->hasParameterOption('--todo')) {
$testSuite->tests->addTestCaseMethodFilter(new TodoTestCaseFilter());
}
$input = new ArgvInput();
$output = new ConsoleOutput(OutputInterface::VERBOSITY_NORMAL, true);
@ -45,9 +39,6 @@ $bootPest = (static function (): void {
'phpunit-argv:',
]);
require_once __DIR__.'/../overrides/Runner/TestSuiteLoader.php';
require_once __DIR__.'/../overrides/Runner/Filter/NameFilterIterator.php';
$composerAutoloadFiles = [
dirname(__DIR__, 3).DIRECTORY_SEPARATOR.'autoload.php',
dirname(__DIR__, 2).DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php',

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\TextUI\Output\Default\ProgressPrinter;
use PHPUnit\Event\Test\Skipped;
use PHPUnit\Event\Test\SkippedSubscriber;
use ReflectionClass;
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*
* This file is overridden to allow Pest Parallel to show todo items in the progress output.
*/
final class TestSkippedSubscriber extends Subscriber implements SkippedSubscriber
{
/**
* Notifies the printer that a test was skipped.
*/
public function notify(Skipped $event): void
{
str_contains($event->message(), '__TODO__')
? $this->printTodoItem()
: $this->printer()->testSkipped();
}
/**
* Prints a "T" to the standard PHPUnit output to indicate a todo item.
*/
private function printTodoItem(): void
{
$mirror = new ReflectionClass($this->printer());
$printerMirror = $mirror->getMethod('printProgress');
$printerMirror->invoke($this->printer(), 'T');
}
}

View File

@ -20,6 +20,7 @@ final class BootOverrides implements Bootstrapper
private const FILES = [
'Runner/Filter/NameFilterIterator.php',
'Runner/TestSuiteLoader.php',
'TextUI/Output/Default/ProgressPrinter/TestSkippedSubscriber.php',
];
/**

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Pest\Plugins;
use JsonException;
use ParaTest\ParaTestCommand;
use Pest\Contracts\Plugins\HandlesArguments;
use Pest\Plugins\Actions\CallsAddsOutput;
@ -29,24 +28,25 @@ final class Parallel implements HandlesArguments
Parallel\Handlers\Pest::class,
Parallel\Handlers\Laravel::class,
];
/**
* @var string[]
*/
private const UNSUPPORTED_ARGUMENTS = ['--todo', '--retry'];
/**
* If the
* Whether the given command line arguments indicate that the test suite should be run in parallel.
*/
public static function isCommand(): bool
public static function isEnabled(): bool
{
// get binary name
Arr::get($_SERVER, 'argv.0');
$argvValue = Arr::get($_ENV, 'PARATEST');
assert(is_string($argvValue) || is_int($argvValue) || is_null($argvValue));
return ((int) $argvValue) === 1;
$argv = new ArgvInput();
if ($argv->hasParameterOption('--parallel')) {
return true;
}
return $argv->hasParameterOption('-p');
}
/**
* If the
* If this code is running in a worker process rather than the main process.
*/
public static function isWorker(): bool
{
@ -57,35 +57,30 @@ final class Parallel implements HandlesArguments
return ((int) $argvValue) === 1;
}
/**
* {@inheritdoc}
*/
public function handleArguments(array $arguments): array
{
if ($this->argumentsContainParallelFlags($arguments)) {
if ($this->hasArgumentsThatWouldBeFasterWithoutParallel()) {
return $this->runTestSuiteInSeries($arguments);
}
if (self::isEnabled()) {
exit($this->runTestSuiteInParallel($arguments));
}
if (self::isWorker()) {
return $this->runWorkersHandlers($arguments);
return $this->runWorkerHandlers($arguments);
}
return $arguments;
}
/**
* @param array<int, string> $arguments
*/
private function argumentsContainParallelFlags(array $arguments): bool
{
if ($this->hasArgument('--parallel', $arguments)) {
return true;
}
return $this->hasArgument('-p', $arguments);
}
/**
* @param array<int, string> $arguments
* Runs the test suite in parallel. This method will exit the process upon completion.
*
* @throws JsonException
* @param array<int, string> $arguments
*/
private function runTestSuiteInParallel(array $arguments): int
{
@ -95,8 +90,6 @@ final class Parallel implements HandlesArguments
return Command::FAILURE;
}
$_ENV['PEST_PARALLEL_ARGV'] = json_encode($_SERVER['argv'], JSON_THROW_ON_ERROR);
$handlers = array_filter(
array_map(fn ($handler): object|string => Container::getInstance()->get($handler), self::HANDLERS),
fn ($handler): bool => $handler instanceof HandlesArguments,
@ -114,10 +107,12 @@ final class Parallel implements HandlesArguments
}
/**
* Runs any handlers that have been registered to handle worker arguments, and returns the modified arguments.
*
* @param array<int, string> $arguments
* @return array<int, string>
*/
private function runWorkersHandlers(array $arguments): array
private function runWorkerHandlers(array $arguments): array
{
$handlers = array_filter(
array_map(fn ($handler): object|string => Container::getInstance()->get($handler), self::HANDLERS),
@ -131,6 +126,9 @@ final class Parallel implements HandlesArguments
);
}
/**
* Outputs a message to the user asking them to install ParaTest as a dev dependency.
*/
private function askUserToInstallParatest(): void
{
/** @var OutputInterface $output */
@ -142,6 +140,9 @@ final class Parallel implements HandlesArguments
]);
}
/**
* Builds an instance of the Paratest command.
*/
private function paratestCommand(): Application
{
/** @var non-empty-string $rootPath */
@ -154,4 +155,34 @@ final class Parallel implements HandlesArguments
return $command;
}
/**
* Whether the command line arguments contain any arguments that are
* not supported or are suboptimal when running in parallel.
*/
private function hasArgumentsThatWouldBeFasterWithoutParallel(): bool
{
$arguments = new ArgvInput();
foreach (self::UNSUPPORTED_ARGUMENTS as $unsupportedArgument) {
if ($arguments->hasParameterOption($unsupportedArgument)) {
return true;
}
}
return false;
}
/**
* Removes any parallel arguments.
*
* @param array<int, string> $arguments
* @return array<int, string>
*/
private function runTestSuiteInSeries(array $arguments): array
{
$arguments = $this->popArgument('--parallel', $arguments);
return $this->popArgument('-p', $arguments);
}
}

View File

@ -21,6 +21,9 @@ final class Laravel implements HandlesArguments
{
use HandleArguments;
/**
* {@inheritdoc}
*/
public function handleArguments(array $arguments): array
{
return self::whenUsingLaravel($arguments, function (array $arguments): array {
@ -43,10 +46,8 @@ final class Laravel implements HandlesArguments
{
$isLaravelApplication = InstalledVersions::isInstalled('laravel/framework', false);
$isLaravelPackage = class_exists(\Orchestra\Testbench\TestCase::class);
if ($isLaravelApplication) {
return $closure($arguments);
}
if ($isLaravelPackage) {
if ($isLaravelApplication && ! $isLaravelPackage) {
return $closure($arguments);
}

View File

@ -22,10 +22,11 @@ final class Parallel implements HandlesArguments
'--parallel',
'-p',
'--no-output',
'--cache-result',
];
/**
* Handles the arguments, removing the ones that are not needed, and adding the "runner" argument.
* Handles the arguments, removing the ones that are not needed, and adds the "runner" argument.
*/
public function handleArguments(array $arguments): array
{

View File

@ -152,8 +152,6 @@ final class ResultPrinter
return;
}
$this->compactPrinter->newLine();
$state = (new StateGenerator())->fromPhpUnitTestResult($testResult);
$this->compactPrinter->errors($state);

View File

@ -34,6 +34,7 @@ final class CompactPrinter
private const LOOKUP_TABLE = [
'.' => ['gray', '.'],
'S' => ['yellow', 's'],
'T' => ['cyan', 't'],
'I' => ['yellow', 'i'],
'N' => ['yellow', 'i'],
'R' => ['yellow', '!'],

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Pest\Plugins;
use Pest\Contracts\Plugins\HandlesArguments;
use Pest\Exceptions\InvalidOption;
/**
* @internal
@ -19,18 +18,14 @@ final class Retry implements HandlesArguments
*/
public function handleArguments(array $arguments): array
{
if ($this->hasArgument('--retry', $arguments)) {
if ($this->hasArgument('--parallel', $arguments)) {
throw new InvalidOption('The --retry option is not supported when running in parallel.');
}
$arguments = $this->popArgument('--retry', $arguments);
$arguments = $this->pushArgument('--order-by=defects', $arguments);
$arguments = $this->pushArgument('--stop-on-failure', $arguments);
if (! $this->hasArgument('--retry', $arguments)) {
return $arguments;
}
return $arguments;
$arguments = $this->popArgument('--retry', $arguments);
$arguments = $this->pushArgument('--order-by=defects', $arguments);
return $this->pushArgument('--stop-on-failure', $arguments);
}
}

View File

@ -33,7 +33,7 @@ final class AfterEachRepository
}
/**
* Gets a after each closure by the given filename.
* Gets an after each closure by the given filename.
*/
public function get(string $filename): Closure
{