Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
PHPUnit Code Sprint with bschulze
  • Loading branch information
MichelHartmann committed Apr 27, 2019
1 parent 8d9234d commit 2f9ae31
Show file tree
Hide file tree
Showing 94 changed files with 1,343 additions and 375 deletions.
17 changes: 17 additions & 0 deletions src/Framework/Exception/IncompatibleReturnValueException.php
@@ -0,0 +1,17 @@
<?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\Framework;

/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class IncompatibleReturnValueException extends Exception
{
}
53 changes: 47 additions & 6 deletions src/Framework/MockObject/Builder/InvocationMocker.php
Expand Up @@ -10,6 +10,8 @@
namespace PHPUnit\Framework\MockObject\Builder;

use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\IncompatibleReturnValueException;
use PHPUnit\Framework\MockObject\ConfigurableMethod;
use PHPUnit\Framework\MockObject\Matcher;
use PHPUnit\Framework\MockObject\Matcher\Invocation;
use PHPUnit\Framework\MockObject\RuntimeException;
Expand All @@ -32,11 +34,11 @@ final class InvocationMocker implements MethodNameMatch
private $matcher;

/**
* @var string[]
* @var ConfigurableMethod[]
*/
private $configurableMethods;

public function __construct(MatcherCollection $collection, Invocation $invocationMatcher, array $configurableMethods)
public function __construct(MatcherCollection $collection, Invocation $invocationMatcher, ConfigurableMethod... $configurableMethods)
{
$this->collection = $collection;
$this->matcher = new Matcher($invocationMatcher);
Expand Down Expand Up @@ -68,11 +70,12 @@ public function will(Stub $stub): Identity
public function willReturn($value, ...$nextValues): self
{
if (\count($nextValues) === 0) {
$this->ensureTypeOfReturnValues([$value]);
$stub = new Stub\ReturnStub($value);
} else {
$stub = new Stub\ConsecutiveCalls(
\array_merge([$value], $nextValues)
);
$values = \array_merge([$value], $nextValues);
$this->ensureTypeOfReturnValues($values);
$stub = new Stub\ConsecutiveCalls($values);
}

return $this->will($stub);
Expand Down Expand Up @@ -193,7 +196,13 @@ public function method($constraint): self
);
}

if (\is_string($constraint) && !\in_array(\strtolower($constraint), $this->configurableMethods, true)) {
$configurableMethodNames = array_map(
function (ConfigurableMethod $configurable) {
return strtolower($configurable->getName());
},
$this->configurableMethods
);
if (\is_string($constraint) && !\in_array(\strtolower($constraint), $configurableMethodNames, true)) {
throw new RuntimeException(
\sprintf(
'Trying to configure method "%s" which cannot be configured because it does not exist, has not been specified, is final, or is static',
Expand Down Expand Up @@ -227,4 +236,36 @@ private function canDefineParameters(): void
);
}
}

private function getConfiguredMethod(): ?ConfigurableMethod
{
$configuredMethod = null;
foreach ($this->configurableMethods as $configurableMethod) {
if ($this->matcher->getMethodNameMatcher()->matchesMethodName($configurableMethod->getName())) {
if ($configuredMethod !== null) {
return null;
}
$configuredMethod = $configurableMethod;
}
}
return $configuredMethod;
}

private function ensureTypeOfReturnValues(array $values): void
{
$configuredMethod = $this->getConfiguredMethod();
if ($configuredMethod === null) {
return;
}
foreach ($values as $value) {
if (!$configuredMethod->mayReturn($value)) {
throw new IncompatibleReturnValueException(sprintf(
'Method %s may not return value of type %s',
$configuredMethod->getName(),
gettype($value)
));
}
}
}

}
39 changes: 39 additions & 0 deletions src/Framework/MockObject/ConfigurableMethod.php
@@ -0,0 +1,39 @@
<?php


namespace PHPUnit\Framework\MockObject;


class ConfigurableMethod
{

/**
* @var string
*/
private $name;

/**
* @var Type
*/
private $returnType;

public function __construct(string $name, Type $returnType)
{
$this->name = $name;
$this->returnType = $returnType;
}

public function getName(): string
{
return $this->name;
}

public function mayReturn($value): bool
{
if (isNull($value) && $this->returnType->allowsNull()) {
return true;
}
return $this->returnType->isAssignable(Type::fromValue($value, false));
}

}
29 changes: 29 additions & 0 deletions src/Framework/MockObject/ConfigurableMethods.php
@@ -0,0 +1,29 @@
<?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\Framework\MockObject;

trait ConfigurableMethods
{

/**
* @var ConfigurableMethods[]
*/
private static $__phpunit_configurableMethods;

public static function __phpunit_initConfigurableMethods(\PHPUnit\Framework\MockObject\ConfigurableMethod... $configurable)
{
if (isset(static::$__phpunit_configurableMethods)) {
// TODO: improve exception
throw new \Exception('nogo!');
}
static::$__phpunit_configurableMethods = $configurable;
}
}
57 changes: 14 additions & 43 deletions src/Framework/MockObject/Generator.php
Expand Up @@ -167,8 +167,7 @@ function ($type) {
);

return $this->getObject(
$mock['code'],
$mock['mockClassName'],
$mock,
$type,
$callOriginalConstructor,
$callAutoload,
Expand Down Expand Up @@ -263,10 +262,8 @@ public function getMockForTrait(string $traitName, array $arguments = [], string
]
);

$this->evalClass(
$classTemplate->render(),
$className['className']
);
$mockClass = new MockTrait($classTemplate->render(), $className['className']);
$mockClass->bringIntoExistence();

return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments);
}
Expand Down Expand Up @@ -303,15 +300,10 @@ public function getObjectForTrait(string $traitName, string $traitClassName = ''
]
);

return $this->getObject($classTemplate->render(), $className['className']);
return $this->getObject(new MockTrait($classTemplate->render(), $className['className']));
}

/**
* @param array|string $type
*
* @throws RuntimeException
*/
public function generate($type, array $methods = null, string $mockClassName = '', bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = true, bool $callOriginalMethods = false): array
public function generate($type, array $methods = null, string $mockClassName = '', bool $callOriginalClone = true, bool $callAutoload = true, bool $cloneArguments = true, bool $callOriginalMethods = false): MockClass
{
if (\is_array($type)) {
\sort($type);
Expand Down Expand Up @@ -522,14 +514,9 @@ private function getInterfaceOwnMethods(string $interfaceName): array
return $methods;
}

/**
* @param array|string $type
*
* @throws RuntimeException
*/
private function getObject(string $code, string $className, $type = '', bool $callOriginalConstructor = false, bool $callAutoload = false, array $arguments = [], bool $callOriginalMethods = false, object $proxyTarget = null, bool $returnValueGeneration = true)
private function getObject(MockBrick $mockClass, $type = '', bool $callOriginalConstructor = false, bool $callAutoload = false, array $arguments = [], bool $callOriginalMethods = false, object $proxyTarget = null, bool $returnValueGeneration = true)
{
$this->evalClass($code, $className);
$className = $mockClass->bringIntoExistence();

if ($callOriginalConstructor &&
\is_string($type) &&
Expand Down Expand Up @@ -586,20 +573,12 @@ private function getObject(string $code, string $className, $type = '', bool $ca
return $object;
}

private function evalClass(string $code, string $className): void
{
if (!\class_exists($className, false)) {
eval($code);
}
}

/**
* @param array|string $type
* @param null|array $explicitMethods
*
* @throws RuntimeException
*/
private function generateMock($type, $explicitMethods, string $mockClassName, bool $callOriginalClone, bool $callAutoload, bool $cloneArguments, bool $callOriginalMethods): array
private function generateMock($type, ?array $explicitMethods, string $mockClassName, bool $callOriginalClone, bool $callAutoload, bool $cloneArguments, bool $callOriginalMethods): MockClass
{
$classTemplate = $this->getTemplate('mocked_class.tpl');

Expand Down Expand Up @@ -821,7 +800,7 @@ private function generateMock($type, $explicitMethods, string $mockClassName, bo

foreach ($mockMethods->asArray() as $mockMethod) {
$mockedMethods .= $mockMethod->generateCode();
$configurable[] = \strtolower($mockMethod->getName());
$configurable[] = new ConfigurableMethod($mockMethod->getName(), $mockMethod->getReturnType());
}

$method = '';
Expand All @@ -843,22 +822,14 @@ private function generateMock($type, $explicitMethods, string $mockClassName, bo
'mock_class_name' => $mockClassName['className'],
'mocked_methods' => $mockedMethods,
'method' => $method,
'configurable' => '[' . \implode(
', ',
\array_map(
function ($m) {
return '\'' . $m . '\'';
},
$configurable
)
) . ']',
]
);

return [
'code' => $classTemplate->render(),
'mockClassName' => $mockClassName['className'],
];
return new MockClass(
$classTemplate->render(),
$mockClassName['className'],
$configurable
);
}

/**
Expand Down
7 changes: 5 additions & 2 deletions src/Framework/MockObject/Generator/mocked_class.tpl.dist
@@ -1,8 +1,11 @@
declare(strict_types=1);

{prologue}{class_declaration}
{
use \PHPUnit\Framework\MockObject\ConfigurableMethods;

private $__phpunit_invocationMocker;
private $__phpunit_originalObject;
private $__phpunit_configurable = {configurable};
private $__phpunit_returnValueGeneration = true;

{clone}{mocked_methods}
Expand All @@ -24,7 +27,7 @@
public function __phpunit_getInvocationMocker(): \PHPUnit\Framework\MockObject\InvocationMocker
{
if ($this->__phpunit_invocationMocker === null) {
$this->__phpunit_invocationMocker = new \PHPUnit\Framework\MockObject\InvocationMocker($this->__phpunit_configurable, $this->__phpunit_returnValueGeneration);
$this->__phpunit_invocationMocker = new \PHPUnit\Framework\MockObject\InvocationMocker(static::$__phpunit_configurableMethods, $this->__phpunit_returnValueGeneration);
}

return $this->__phpunit_invocationMocker;
Expand Down
4 changes: 2 additions & 2 deletions src/Framework/MockObject/Generator/mocked_method.tpl.dist
@@ -1,5 +1,5 @@

{modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type}
{modifier} function {reference}{method_name}({arguments_decl}){return_declaration}
{{deprecation}
$__phpunit_arguments = [{arguments_call}];
$__phpunit_count = func_num_args();
Expand All @@ -14,7 +14,7 @@

$__phpunit_result = $this->__phpunit_getInvocationMocker()->invoke(
new \PHPUnit\Framework\MockObject\Invocation(
'{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}
'{class_name}', '{method_name}', $__phpunit_arguments, '{return_declaration}', $this, {clone_arguments}
)
);

Expand Down
@@ -1,5 +1,5 @@

{modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type}
{modifier} function {reference}{method_name}({arguments_decl}){return_declaration}
{{deprecation}
$__phpunit_arguments = [{arguments_call}];
$__phpunit_count = func_num_args();
Expand All @@ -14,7 +14,7 @@

$this->__phpunit_getInvocationMocker()->invoke(
new \PHPUnit\Framework\MockObject\Invocation(
'{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}
'{class_name}', '{method_name}', $__phpunit_arguments, '{return_declaration}', $this, {clone_arguments}
)
);
}
@@ -1,5 +1,5 @@

{modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type}
{modifier} function {reference}{method_name}({arguments_decl}){return_declaration}
{
throw new \PHPUnit\Framework\MockObject\BadMethodCallException('Static method "{method_name}" cannot be invoked on mock object');
}
4 changes: 2 additions & 2 deletions src/Framework/MockObject/Generator/proxied_method.tpl.dist
@@ -1,5 +1,5 @@

{modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type}
{modifier} function {reference}{method_name}({arguments_decl}){return_declaration}
{
$__phpunit_arguments = [{arguments_call}];
$__phpunit_count = func_num_args();
Expand All @@ -14,7 +14,7 @@

$this->__phpunit_getInvocationMocker()->invoke(
new \PHPUnit\Framework\MockObject\Invocation(
'{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true
'{class_name}', '{method_name}', $__phpunit_arguments, '{return_declaration}', $this, {clone_arguments}, true
)
);

Expand Down
@@ -1,5 +1,5 @@

{modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type}
{modifier} function {reference}{method_name}({arguments_decl}){return_declaration}
{
$__phpunit_arguments = [{arguments_call}];
$__phpunit_count = func_num_args();
Expand All @@ -14,7 +14,7 @@

$this->__phpunit_getInvocationMocker()->invoke(
new \PHPUnit\Framework\MockObject\Invocation(
'{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments}, true
'{class_name}', '{method_name}', $__phpunit_arguments, '{return_declaration}', $this, {clone_arguments}, true
)
);

Expand Down
2 changes: 2 additions & 0 deletions src/Framework/MockObject/Generator/trait_class.tpl.dist
@@ -1,3 +1,5 @@
declare(strict_types=1);

{prologue}class {class_name}
{
use {trait_name};
Expand Down

0 comments on commit 2f9ae31

Please sign in to comment.