Merge branch '4.4' into 5.1

* 4.4:
  [HttpKernel] Adjust tests to new "class not found" error message format.
  Don't call createMock with an array of interfaces.
  [FrameworkBundle] Fix Tests on PHPUnit 9.3.
  [WebProfilerBundle] Fix Tests on PHPUnit 9.3.
  esmtp error not being thrown properly
  [Yaml Parser] fixed Parser to skip comments when inlining sequences
  [DI] fix generating preload file when cache_dir is outside project_dir
  Fix CacheCollectorPass with decorated cache pools
  [PhpUnitBridge] CoverageListenerTrait update for PHPUnit 8.5/9.x
  Remove invalid instantiation declaration
  [PropertyInfo] Fix typed collections in PHP 7.4
This commit is contained in:
Fabien Potencier 2020-09-07 07:10:28 +02:00
commit 0bdef0a996
19 changed files with 197 additions and 58 deletions

View File

@ -70,7 +70,7 @@ class EntityUserProviderTest extends TestCase
{
$user = new User(1, 1, 'user1');
$repository = $this->createMock([ObjectRepository::class, UserLoaderInterface::class]);
$repository = $this->createMock(UserLoaderRepository::class);
$repository
->expects($this->once())
->method('loadUserByUsername')
@ -156,7 +156,7 @@ class EntityUserProviderTest extends TestCase
public function testLoadUserByUserNameShouldLoadUserWhenProperInterfaceProvided()
{
$repository = $this->createMock([ObjectRepository::class, UserLoaderInterface::class]);
$repository = $this->createMock(UserLoaderRepository::class);
$repository->expects($this->once())
->method('loadUserByUsername')
->with('name')
@ -189,7 +189,7 @@ class EntityUserProviderTest extends TestCase
{
$user = new User(1, 1, 'user1');
$repository = $this->createMock([interface_exists(ObjectRepository::class) ? ObjectRepository::class : LegacyObjectRepository::class, PasswordUpgraderInterface::class]);
$repository = $this->createMock(PasswordUpgraderRepository::class);
$repository->expects($this->once())
->method('upgradePassword')
->with($user, 'foobar');
@ -233,3 +233,11 @@ class EntityUserProviderTest extends TestCase
]);
}
}
abstract class UserLoaderRepository implements ObjectRepository, UserLoaderInterface
{
}
abstract class PasswordUpgraderRepository implements ObjectRepository, PasswordUpgraderInterface
{
}

View File

@ -13,6 +13,7 @@ namespace Symfony\Bridge\PhpUnit\Legacy;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Warning;
use PHPUnit\Util\Annotation\Registry;
use PHPUnit\Util\Test;
/**
@ -66,9 +67,6 @@ class CoverageListenerTrait
return;
}
$r = new \ReflectionProperty(Test::class, 'annotationCache');
$r->setAccessible(true);
$covers = $sutFqcn;
if (!\is_array($sutFqcn)) {
$covers = [$sutFqcn];
@ -78,15 +76,42 @@ class CoverageListenerTrait
}
}
if (class_exists(Registry::class)) {
$this->addCoversForDocBlockInsideRegistry($test, $covers);
return;
}
$this->addCoversForClassToAnnotationCache($test, $covers);
}
private function addCoversForClassToAnnotationCache($test, $covers)
{
$r = new \ReflectionProperty(Test::class, 'annotationCache');
$r->setAccessible(true);
$cache = $r->getValue();
$cache = array_replace_recursive($cache, [
\get_class($test) => [
'covers' => $covers,
],
]);
$r->setValue(Test::class, $cache);
}
private function addCoversForDocBlockInsideRegistry($test, $covers)
{
$docBlock = Registry::getInstance()->forClassName(\get_class($test));
$symbolAnnotations = new \ReflectionProperty($docBlock, 'symbolAnnotations');
$symbolAnnotations->setAccessible(true);
$symbolAnnotations->setValue($docBlock, array_replace($docBlock->symbolAnnotations(), [
'covers' => $covers,
]));
}
private function findSutFqcn($test)
{
if ($this->sutFqcnResolver) {

View File

@ -71,7 +71,7 @@ class WebTestCaseTest extends TestCase
{
$this->getResponseTester(new Response('', 302, ['Location' => 'https://example.com/']))->assertResponseRedirects('https://example.com/', 302);
$this->expectException(AssertionFailedError::class);
$this->expectExceptionMessage('is redirected and has header "Location" with value "https://example.com/" and status code is 301.');
$this->expectExceptionMessageMatches('#(:?\( )?is redirected and has header "Location" with value "https://example\.com/" (:?\) )?and status code is 301\.#');
$this->getResponseTester(new Response('', 302))->assertResponseRedirects('https://example.com/', 301);
}

View File

@ -382,10 +382,12 @@ class ProfilerControllerTest extends WebTestCase
->with($profile->getToken())
->willReturn($profile);
$collectorsNames = array_keys($profile->getCollectors());
$profiler
->expects($this->atLeastOnce())
->method('has')
->with($this->logicalXor($collectorsNames = array_keys($profile->getCollectors())))
->with($this->logicalXor(...$collectorsNames))
->willReturn(true);
$expectedTemplate = 'expected_template.html.twig';

View File

@ -47,15 +47,13 @@ class CacheCollectorPass implements CompilerPassInterface
}
foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $attributes) {
$this->addToCollector($id, $container);
$poolName = $attributes[0]['name'] ?? $id;
if (($attributes[0]['name'] ?? $id) !== $id) {
$this->addToCollector($attributes[0]['name'], $container);
}
$this->addToCollector($id, $poolName, $container);
}
}
private function addToCollector(string $id, ContainerBuilder $container)
private function addToCollector(string $id, string $name, ContainerBuilder $container)
{
$definition = $container->getDefinition($id);
if ($definition->isAbstract()) {
@ -77,7 +75,7 @@ class CacheCollectorPass implements CompilerPassInterface
$container->setDefinition($id, $recorder);
// Tell the collector to add the new instance
$collectorDefinition->addMethodCall('addInstance', [$id, new Reference($id)]);
$collectorDefinition->addMethodCall('addInstance', [$name, new Reference($id)]);
$collectorDefinition->setPublic(false);
}
}

View File

@ -16,8 +16,8 @@ use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\ChainAdapter;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\Tests\Fixtures\ExternalAdapter;
use Symfony\Component\Cache\Tests\Fixtures\PrunableAdapter;
/**
* @author Kévin Dunglas <dunglas@gmail.com>
@ -189,7 +189,7 @@ class ChainAdapterTest extends AdapterTestCase
private function getPruneableMock(): AdapterInterface
{
$pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]);
$pruneable = $this->createMock(PrunableAdapter::class);
$pruneable
->expects($this->atLeastOnce())
@ -201,7 +201,7 @@ class ChainAdapterTest extends AdapterTestCase
private function getFailingPruneableMock(): AdapterInterface
{
$pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]);
$pruneable = $this->createMock(PrunableAdapter::class);
$pruneable
->expects($this->atLeastOnce())

View File

@ -18,7 +18,7 @@ use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\Tests\Fixtures\PrunableAdapter;
use Symfony\Component\Cache\Tests\Traits\TagAwareTestTrait;
/**
@ -204,7 +204,7 @@ class TagAwareAdapterTest extends AdapterTestCase
*/
private function getPruneableMock(): AdapterInterface
{
$pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]);
$pruneable = $this->createMock(PrunableAdapter::class);
$pruneable
->expects($this->atLeastOnce())
@ -216,7 +216,7 @@ class TagAwareAdapterTest extends AdapterTestCase
private function getFailingPruneableMock(): AdapterInterface
{
$pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]);
$pruneable = $this->createMock(PrunableAdapter::class);
$pruneable
->expects($this->atLeastOnce())

View File

@ -19,6 +19,8 @@ use Symfony\Component\Cache\Adapter\TraceableAdapter;
use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter;
use Symfony\Component\Cache\DataCollector\CacheDataCollector;
use Symfony\Component\Cache\DependencyInjection\CacheCollectorPass;
use Symfony\Component\Cache\Tests\Fixtures\ArrayCache;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
@ -48,16 +50,51 @@ class CacheCollectorPassTest extends TestCase
$this->assertEquals([
['addInstance', ['fs', new Reference('fs')]],
['addInstance', ['tagged_fs', new Reference('tagged_fs')]],
['addInstance', ['.php.inner', new Reference('.php.inner')]],
['addInstance', ['php', new Reference('php')]],
['addInstance', ['php', new Reference('.php.inner')]],
], $collector->getMethodCalls());
$this->assertSame(TraceableAdapter::class, $container->findDefinition('fs')->getClass());
$this->assertSame(TraceableTagAwareAdapter::class, $container->getDefinition('tagged_fs')->getClass());
$this->assertSame(TraceableAdapter::class, $container->findDefinition('.php.inner')->getClass());
$this->assertSame(TraceableTagAwareAdapter::class, $container->getDefinition('php')->getClass());
$this->assertSame(TagAwareAdapter::class, $container->getDefinition('php')->getClass());
$this->assertFalse($collector->isPublic(), 'The "data_collector.cache" should be private after processing');
}
public function testProcessCacheObjectsAreDecorated()
{
$container = new ContainerBuilder();
$collector = $container->register('data_collector.cache', CacheDataCollector::class);
$container
->register('cache.object', ArrayCache::class)
->addTag('cache.pool', ['name' => 'cache.object']);
$container
->register('something_is_decorating_cache_object', TagAwareAdapter::class)
->setPublic(true)
->setDecoratedService('cache.object');
$container->register('some_service_using_cache_object', TraceableAdapter::class)
->setPublic(true)
->addArgument(new Reference('cache.object'));
$container->addCompilerPass(new CacheCollectorPass(), PassConfig::TYPE_BEFORE_REMOVING);
$container->compile();
$this->assertCount(1, $collector->getMethodCalls());
$this->assertEquals(
[
[
'addInstance',
[
'cache.object',
new Reference('something_is_decorating_cache_object'),
],
],
],
$collector->getMethodCalls()
);
}
}

View File

@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Tests\Fixtures;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\PruneableInterface;
abstract class PrunableAdapter implements AdapterInterface, PruneableInterface
{
}

View File

@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Tests\Fixtures;
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Cache\PruneableInterface;
abstract class PrunableCache implements CacheInterface, PruneableInterface
{
}

View File

@ -326,7 +326,7 @@ EOF;
$this->asFiles = false;
if ($this->preload && null !== $autoloadFile = $this->getAutoloadFile()) {
$autoloadFile = substr($this->export($autoloadFile), 2, -1);
$autoloadFile = trim($this->export($autoloadFile), '()\\');
$preloadedFiles = array_reverse($preloadedFiles);
$preloadedFiles = implode("';\nrequire __DIR__.'/", $preloadedFiles);
@ -2123,9 +2123,7 @@ EOF;
private function getAutoloadFile(): ?string
{
if (null === $this->targetDirRegex) {
return null;
}
$file = null;
foreach (spl_autoload_functions() as $autoloader) {
if (!\is_array($autoloader)) {
@ -2144,14 +2142,14 @@ EOF;
if (0 === strpos($class, 'ComposerAutoloaderInit') && $class::getLoader() === $autoloader[0]) {
$file = \dirname((new \ReflectionClass($class))->getFileName(), 2).'/autoload.php';
if (preg_match($this->targetDirRegex.'A', $file)) {
if (null !== $this->targetDirRegex && preg_match($this->targetDirRegex.'A', $file)) {
return $file;
}
}
}
}
return null;
return $file;
}
private function getClasses(Definition $definition, string $id): array

View File

@ -16,18 +16,6 @@ use Symfony\Component\Form\Extension\Core\DataTransformer\StringToFloatTransform
class StringToFloatTransformerTest extends TestCase
{
private $transformer;
protected function setUp(): void
{
$this->transformer = new StringToFloatTransformer();
}
protected function tearDown(): void
{
$this->transformer = null;
}
public function provideTransformations(): array
{
return [

View File

@ -170,9 +170,9 @@ class ControllerResolverTest extends TestCase
$controller = new ControllerTest();
return [
['foo', \Error::class, 'Class \'foo\' not found'],
['oof::bar', \Error::class, 'Class \'oof\' not found'],
[['oof', 'bar'], \Error::class, 'Class \'oof\' not found'],
['foo', \Error::class, \PHP_VERSION_ID < 80000 ? 'Class \'foo\' not found' : 'Class "foo" not found'],
['oof::bar', \Error::class, \PHP_VERSION_ID < 80000 ? 'Class \'oof\' not found' : 'Class "oof" not found'],
[['oof', 'bar'], \Error::class, \PHP_VERSION_ID < 80000 ? 'Class \'oof\' not found' : 'Class "oof" not found'],
['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::staticsAction', \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'],
['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::privateAction', \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::protectedAction', \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Method "protectedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],

View File

@ -166,7 +166,11 @@ class EsmtpTransport extends SmtpTransport
return;
} catch (TransportExceptionInterface $e) {
$this->executeCommand("RSET\r\n", [250]);
try {
$this->executeCommand("RSET\r\n", [250]);
} catch (TransportExceptionInterface $_) {
// ignore this exception as it probably means that the server error was final
}
// keep the error message, but tries the other authenticators
$errors[$authenticator->getAuthKeyword()] = $e;

View File

@ -130,18 +130,6 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
*/
public function getTypes(string $class, string $property, array $context = []): ?array
{
if (\PHP_VERSION_ID >= 70400) {
try {
$reflectionProperty = new \ReflectionProperty($class, $property);
$type = $reflectionProperty->getType();
if (null !== $type) {
return $this->extractFromReflectionType($type, $reflectionProperty->getDeclaringClass());
}
} catch (\ReflectionException $e) {
// noop
}
}
if ($fromMutator = $this->extractFromMutator($class, $property)) {
return $fromMutator;
}
@ -161,6 +149,18 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
return $fromDefaultValue;
}
if (\PHP_VERSION_ID >= 70400) {
try {
$reflectionProperty = new \ReflectionProperty($class, $property);
$type = $reflectionProperty->getType();
if (null !== $type) {
return $this->extractFromReflectionType($type, $reflectionProperty->getDeclaringClass());
}
} catch (\ReflectionException $e) {
// noop
}
}
return null;
}

View File

@ -414,6 +414,7 @@ class ReflectionExtractorTest extends TestCase
{
$this->assertEquals([new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)], $this->extractor->getTypes(Php74Dummy::class, 'dummy'));
$this->assertEquals([new Type(Type::BUILTIN_TYPE_BOOL, true)], $this->extractor->getTypes(Php74Dummy::class, 'nullableBoolProp'));
$this->assertEquals([new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_STRING))], $this->extractor->getTypes(Php74Dummy::class, 'stringCollection'));
}
/**

View File

@ -18,4 +18,14 @@ class Php74Dummy
{
public Dummy $dummy;
private ?bool $nullableBoolProp;
/** @var string[] */
private array $stringCollection;
public function addStringCollection(string $string): void
{
}
public function removeStringCollection(string $string): void
{
}
}

View File

@ -1253,7 +1253,13 @@ class Parser
for ($i = 1; isset($this->currentLine[$i]) && ']' !== $this->currentLine[$i]; ++$i) {
}
$value .= trim($this->currentLine);
$trimmedValue = trim($this->currentLine);
if ('' !== $trimmedValue && '#' === $trimmedValue[0]) {
continue;
}
$value .= $trimmedValue;
if (isset($this->currentLine[$i]) && ']' === $this->currentLine[$i]) {
break;

View File

@ -1897,6 +1897,30 @@ YAML
[new TaggedValue('foo', 'bar')],
'[ !foo bar ]',
],
'with-comments' => [
[
[new TaggedValue('foo', ['foo', 'baz'])],
],
<<<YAML
- [!foo [
foo,
baz
#bar
]]
YAML
],
'with-comments-trailing-comma' => [
[
[new TaggedValue('foo', ['foo', 'baz'])],
],
<<<YAML
- [!foo [
foo,
baz,
#bar
]]
YAML
],
];
}