Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject;

use function sprintf;

/**
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class NoMoreParameterSetsConfiguredException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(Invocation $invocation, int $numberOfConfiguredParameterSets)
{
parent::__construct(
sprintf(
'Not enough parameter sets configured, only %d parameter sets given for %s::%s()',
$numberOfConfiguredParameterSets,
$invocation->className(),
$invocation->methodName(),
),
);
}
}
10 changes: 10 additions & 0 deletions src/Framework/MockObject/Runtime/Interface/InvocationStubber.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ public function after(string $id): self;
*/
public function with(mixed ...$arguments): self;

/**
* @return $this
*/
public function withConsecutiveParameterSets(mixed ...$arguments): self;

/**
* @return $this
*/
public function withParameterSetsInAnyOrder(mixed ...$arguments): self;

/**
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,24 @@ public function with(mixed ...$arguments): InvocationStubber
return $this;
}

public function withConsecutiveParameterSets(mixed ...$arguments): InvocationStubber
{
$this->ensureParametersCanBeConfigured();

$this->matcher->setParametersRule(new Rule\ConsecutiveParameterSets($arguments));

return $this;
}

public function withParameterSetsInAnyOrder(mixed ...$arguments): InvocationStubber
{
$this->ensureParametersCanBeConfigured();

$this->matcher->setParametersRule(new Rule\UnorderedParameterSets($arguments));

return $this;
}

/**
* @throws MethodNameNotConfiguredException
* @throws MethodParametersAlreadyConfiguredException
Expand Down
87 changes: 87 additions & 0 deletions src/Framework/MockObject/Runtime/Rule/ConsecutiveParameterSets.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;

use function array_shift;
use function count;
use function is_array;
use function sprintf;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
use PHPUnit\Framework\MockObject\NoMoreParameterSetsConfiguredException;

/**
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ConsecutiveParameterSets implements ParametersRule
{
/**
* @var list<Parameters>
*/
private array $stack = [];

/**
* @var list<Parameters>
*/
private array $applied = [];
private int $numberOfConfiguredParameterSets = 0;

/**
* @param list<Parameters> $stack
*/
public function __construct(array $stack)
{
foreach ($stack as $parameters) {
$this->stack[] = new Parameters(is_array($parameters) ? $parameters : [$parameters]);
}
$this->numberOfConfiguredParameterSets = count($stack);
}

public function apply(BaseInvocation $invocation): void
{
if ($this->stack === []) {
throw new NoMoreParameterSetsConfiguredException(
$invocation,
$this->numberOfConfiguredParameterSets,
);
}

$parameters = array_shift($this->stack);
$this->applied[] = $parameters;
$parameters->apply($invocation);
}

/**
* Checks if the invocation $invocation matches the current rules. If it
* does the rule will get the invoked() method called which should check
* if an expectation is met.
*
* @throws ExpectationFailedException
*/
public function verify(): void
{
if (count($this->applied) !== $this->numberOfConfiguredParameterSets &&
count($this->stack) > 0) {
throw new ExpectationFailedException(
sprintf(
'Too much parameter sets given, %d from %d expected parameter sets have been called.',
count($this->applied),
$this->numberOfConfiguredParameterSets,
),
);
}

foreach ($this->applied as $parameters) {
$parameters->verify();
}
}
}
12 changes: 11 additions & 1 deletion src/Framework/MockObject/Runtime/Rule/Parameters.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

use function count;
use function sprintf;
use AllowDynamicProperties;
use Exception;
use PHPUnit\Framework\Constraint\Callback;
use PHPUnit\Framework\Constraint\Constraint;
Expand All @@ -25,14 +26,15 @@
*
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class Parameters implements ParametersRule
#[AllowDynamicProperties] final class Parameters implements ParametersRule
{
/**
* @var list<Constraint>
*/
private array $parameters = [];
private ?BaseInvocation $invocation = null;
private null|bool|ExpectationFailedException $parameterVerificationResult;
private bool $useAssertionCount = true;

/**
* @param array<mixed> $parameters
Expand Down Expand Up @@ -81,6 +83,11 @@ public function verify(): void
$this->doVerify();
}

public function useAssertionCount(bool $useAssertionCount): void
{
$this->useAssertionCount = $useAssertionCount;
}

/**
* @throws ExpectationFailedException
*/
Expand Down Expand Up @@ -149,6 +156,9 @@ private function guardAgainstDuplicateEvaluationOfParameterConstraints(): bool

private function incrementAssertionCount(): void
{
if ($this->useAssertionCount === false) {
return;
}
Test::currentTestCase()->addToAssertionCount(1);
}
}
110 changes: 110 additions & 0 deletions src/Framework/MockObject/Runtime/Rule/UnorderedParameterSets.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPUnit\Framework\MockObject\Rule;

use function array_search;
use function array_shift;
use function count;
use function implode;
use function is_array;
use function sprintf;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\Invocation as BaseInvocation;
use PHPUnit\Framework\MockObject\NoMoreParameterSetsConfiguredException;

final class UnorderedParameterSets implements ParametersRule
{
/**
* @var list<Parameters>
*/
private array $stack = [];

/**
* @var list<Parameters>
*/
private array $unapplied = [];

/**
* @var list<Parameters>
*/
private array $applied = [];
private int $numberOfConfiguredParameterSets = 0;

/**
* @param list<Parameters> $stack
*/
public function __construct(array $stack)
{
foreach ($stack as $parameters) {
$this->stack[] = new Parameters(is_array($parameters) ? $parameters : [$parameters]);
}
$this->unapplied = $this->stack;
$this->numberOfConfiguredParameterSets = count($stack);
}

public function apply(BaseInvocation $invocation): void
{
if ($this->unapplied === []) {
throw new NoMoreParameterSetsConfiguredException(
$invocation,
$this->numberOfConfiguredParameterSets,
);
}

$checkedParameters = 0;
$unappliedParameters = count($this->unapplied);

while ($checkedParameters < $unappliedParameters) {
$checkedParameters++;
$parameters = array_shift($this->unapplied);

try {
$parameters->useAssertionCount(false);
$parameters->apply($invocation);

$this->applied[] = $parameters;

$parameters->useAssertionCount(true);
$parameters->apply($invocation);

break;
} catch (ExpectationFailedException $e) {
$this->unapplied[] = $parameters;
}
}
}

/**
* Checks if the invocation $invocation matches the current rules. If it
* does the rule will get the invoked() method called which should check
* if an expectation is met.
*
* @throws ExpectationFailedException
*/
public function verify(): void
{
if (count($this->applied) !== $this->numberOfConfiguredParameterSets &&
count($this->unapplied) > 0) {
$unappliedIndexes = [];

foreach ($this->unapplied as $parameters) {
$unappliedIndexes[] = array_search($parameters, $this->stack, true);
}

throw new ExpectationFailedException(
sprintf(
'%d from %d expected parameter sets were called, indexes [' . implode(', ', $unappliedIndexes) . '] were not called.',
count($this->applied),
$this->numberOfConfiguredParameterSets,
),
);
}
}
}
21 changes: 21 additions & 0 deletions tests/end-to-end/regression/6407-multiple-arguments.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
https://github.com/sebastianbergmann/phpunit/issues/6407
--FILE--
<?php declare(strict_types=1);
$_SERVER['argv'][] = '--do-not-cache-result';
$_SERVER['argv'][] = '--no-configuration';
$_SERVER['argv'][] = __DIR__ . '/6407/Issue6407MultipleArgumentTest.php';

require_once __DIR__ . '/../../bootstrap.php';

(new PHPUnit\TextUI\Application)->run($_SERVER['argv']);
--EXPECTF--
PHPUnit %s by Sebastian Bergmann and contributors.

Runtime: %s

.. 2 / 2 (100%)

Time: %s, Memory: %s

OK (2 tests, 10 assertions)
21 changes: 21 additions & 0 deletions tests/end-to-end/regression/6407-single-arguments.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
https://github.com/sebastianbergmann/phpunit/issues/6407
--FILE--
<?php declare(strict_types=1);
$_SERVER['argv'][] = '--do-not-cache-result';
$_SERVER['argv'][] = '--no-configuration';
$_SERVER['argv'][] = __DIR__ . '/6407/Issue6407SingleArgumentTest.php';

require_once __DIR__ . '/../../bootstrap.php';

(new PHPUnit\TextUI\Application)->run($_SERVER['argv']);
--EXPECTF--
PHPUnit %s by Sebastian Bergmann and contributors.

Runtime: %s

.. 2 / 2 (100%)

Time: %s, Memory: %s

OK (2 tests, 6 assertions)
Loading