Initial Drupal 11 with DDEV setup
This commit is contained in:
		@ -0,0 +1,68 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery;
 | 
			
		||||
use Drupal\Component\FileCache\FileCacheFactory;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery
 | 
			
		||||
 * @group Annotation
 | 
			
		||||
 * @runTestsInSeparateProcesses
 | 
			
		||||
 */
 | 
			
		||||
class AnnotatedClassDiscoveryCachedTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
    // Ensure FileCacheFactory::DISABLE_CACHE is *not* set, since we're testing
 | 
			
		||||
    // integration with the file cache.
 | 
			
		||||
    FileCacheFactory::setConfiguration([]);
 | 
			
		||||
    // Ensure that FileCacheFactory has a prefix.
 | 
			
		||||
    FileCacheFactory::setPrefix('prefix');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that getDefinitions() retrieves the file cache correctly.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getDefinitions
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetDefinitions(): void {
 | 
			
		||||
    // Path to the classes which we'll discover and parse annotation.
 | 
			
		||||
    $discovery_path = __DIR__ . '/Fixtures';
 | 
			
		||||
    // File path that should be discovered within that directory.
 | 
			
		||||
    $file_path = $discovery_path . '/PluginNamespace/DiscoveryTest1.php';
 | 
			
		||||
 | 
			
		||||
    $discovery = new AnnotatedClassDiscovery(['com\example' => [$discovery_path]]);
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'discovery_test_1' => [
 | 
			
		||||
        'id' => 'discovery_test_1',
 | 
			
		||||
        'class' => 'com\example\PluginNamespace\DiscoveryTest1',
 | 
			
		||||
      ],
 | 
			
		||||
    ], $discovery->getDefinitions());
 | 
			
		||||
 | 
			
		||||
    // Gain access to the file cache so we can change it.
 | 
			
		||||
    $ref_file_cache = new \ReflectionProperty($discovery, 'fileCache');
 | 
			
		||||
    /** @var \Drupal\Component\FileCache\FileCacheInterface $file_cache */
 | 
			
		||||
    $file_cache = $ref_file_cache->getValue($discovery);
 | 
			
		||||
    // The file cache is keyed by the file path, and we'll add some known
 | 
			
		||||
    // content to test against.
 | 
			
		||||
    $file_cache->set($file_path, [
 | 
			
		||||
      'id' => 'wrong_id',
 | 
			
		||||
      'content' => serialize(['an' => 'array']),
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // Now perform the same query and check for the cached results.
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'wrong_id' => [
 | 
			
		||||
        'an' => 'array',
 | 
			
		||||
      ],
 | 
			
		||||
    ], $discovery->getDefinitions());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,101 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Annotation\Plugin;
 | 
			
		||||
use Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery;
 | 
			
		||||
use Drupal\Component\FileCache\FileCacheFactory;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery
 | 
			
		||||
 * @group Annotation
 | 
			
		||||
 * @runTestsInSeparateProcesses
 | 
			
		||||
 */
 | 
			
		||||
class AnnotatedClassDiscoveryTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
    // Ensure the file cache is disabled.
 | 
			
		||||
    FileCacheFactory::setConfiguration([FileCacheFactory::DISABLE_CACHE => TRUE]);
 | 
			
		||||
    // Ensure that FileCacheFactory has a prefix.
 | 
			
		||||
    FileCacheFactory::setPrefix('prefix');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::__construct
 | 
			
		||||
   * @covers ::getPluginNamespaces
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetPluginNamespaces(): void {
 | 
			
		||||
    $discovery = new AnnotatedClassDiscovery(['com/example' => [__DIR__]]);
 | 
			
		||||
 | 
			
		||||
    $reflection = new \ReflectionMethod($discovery, 'getPluginNamespaces');
 | 
			
		||||
    $result = $reflection->invoke($discovery);
 | 
			
		||||
    $this->assertEquals(['com/example' => [__DIR__]], $result);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getDefinitions
 | 
			
		||||
   * @covers ::prepareAnnotationDefinition
 | 
			
		||||
   * @covers ::getAnnotationReader
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetDefinitions(): void {
 | 
			
		||||
    $discovery = new AnnotatedClassDiscovery(['com\example' => [__DIR__ . '/Fixtures']]);
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'discovery_test_1' => [
 | 
			
		||||
        'id' => 'discovery_test_1',
 | 
			
		||||
        'class' => 'com\example\PluginNamespace\DiscoveryTest1',
 | 
			
		||||
      ],
 | 
			
		||||
    ], $discovery->getDefinitions());
 | 
			
		||||
 | 
			
		||||
    $custom_annotation_discovery = new AnnotatedClassDiscovery(['com\example' => [__DIR__ . '/Fixtures']], CustomPlugin::class, ['Drupal\Tests\Component\Annotation']);
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'discovery_test_1' => [
 | 
			
		||||
        'id' => 'discovery_test_1',
 | 
			
		||||
        'class' => 'com\example\PluginNamespace\DiscoveryTest1',
 | 
			
		||||
        'title' => 'Discovery test plugin',
 | 
			
		||||
      ],
 | 
			
		||||
    ], $custom_annotation_discovery->getDefinitions());
 | 
			
		||||
 | 
			
		||||
    $empty_discovery = new AnnotatedClassDiscovery(['com\example' => [__DIR__ . '/Fixtures']], CustomPlugin2::class, ['Drupal\Tests\Component\Annotation']);
 | 
			
		||||
    $this->assertEquals([], $empty_discovery->getDefinitions());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Custom plugin annotation.
 | 
			
		||||
 *
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 */
 | 
			
		||||
class CustomPlugin extends Plugin {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The plugin ID.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   */
 | 
			
		||||
  public $id;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The plugin title.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   *
 | 
			
		||||
   * @ingroup plugin_translatable
 | 
			
		||||
   */
 | 
			
		||||
  public $title = '';
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Custom plugin annotation.
 | 
			
		||||
 *
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 */
 | 
			
		||||
class CustomPlugin2 extends Plugin {}
 | 
			
		||||
@ -0,0 +1,57 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Annotation\AnnotationBase;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Annotation\AnnotationBase
 | 
			
		||||
 * @group Annotation
 | 
			
		||||
 */
 | 
			
		||||
class AnnotationBaseTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getProvider
 | 
			
		||||
   * @covers ::setProvider
 | 
			
		||||
   */
 | 
			
		||||
  public function testSetProvider(): void {
 | 
			
		||||
    $plugin = new AnnotationBaseStub();
 | 
			
		||||
    $plugin->setProvider('example');
 | 
			
		||||
    $this->assertEquals('example', $plugin->getProvider());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getId
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetId(): void {
 | 
			
		||||
    $plugin = new AnnotationBaseStub();
 | 
			
		||||
    // Doctrine sets the public prop directly.
 | 
			
		||||
    $plugin->id = 'example';
 | 
			
		||||
    $this->assertEquals('example', $plugin->getId());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getClass
 | 
			
		||||
   * @covers ::setClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testSetClass(): void {
 | 
			
		||||
    $plugin = new AnnotationBaseStub();
 | 
			
		||||
    $plugin->setClass('example');
 | 
			
		||||
    $this->assertEquals('example', $plugin->getClass());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * {@inheritdoc}
 | 
			
		||||
 */
 | 
			
		||||
class AnnotationBaseStub extends AnnotationBase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function get() {}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,45 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Annotation\Doctrine\DocParser;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Annotation\Doctrine\DocParser
 | 
			
		||||
 *
 | 
			
		||||
 * @group Annotation
 | 
			
		||||
 */
 | 
			
		||||
class DocParserIgnoredClassesTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Ensure annotations can be ignored when namespaces are present.
 | 
			
		||||
   *
 | 
			
		||||
   * Drupal's DocParser should never use class_exists() on an ignored
 | 
			
		||||
   * annotation, including cases where namespaces are set.
 | 
			
		||||
   */
 | 
			
		||||
  public function testIgnoredAnnotationSkippedBeforeReflection(): void {
 | 
			
		||||
    $annotation = 'neverReflectThis';
 | 
			
		||||
    $parser = new DocParser();
 | 
			
		||||
    $parser->setIgnoredAnnotationNames([$annotation => TRUE]);
 | 
			
		||||
    $parser->addNamespace('\\Arbitrary\\Namespace');
 | 
			
		||||
 | 
			
		||||
    // Register our class loader which will fail if the parser tries to
 | 
			
		||||
    // autoload disallowed annotations.
 | 
			
		||||
    $autoloader = function ($class_name) use ($annotation) {
 | 
			
		||||
      $name_array = explode('\\', $class_name);
 | 
			
		||||
      $name = array_pop($name_array);
 | 
			
		||||
      if ($name == $annotation) {
 | 
			
		||||
        $this->fail('Attempted to autoload an ignored annotation: ' . $name);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    spl_autoload_register($autoloader, TRUE, TRUE);
 | 
			
		||||
    // Perform the parse.
 | 
			
		||||
    $this->assertEmpty($parser->parse('@neverReflectThis'));
 | 
			
		||||
    // Clean up after ourselves.
 | 
			
		||||
    spl_autoload_unregister($autoloader);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Annotation;
 | 
			
		||||
 | 
			
		||||
/** @Annotation */
 | 
			
		||||
class AnnotWithDefaultValue
 | 
			
		||||
{
 | 
			
		||||
    /** @var string */
 | 
			
		||||
    public $foo = 'bar';
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,12 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Annotation;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 */
 | 
			
		||||
class Autoload
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,13 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Annotation;
 | 
			
		||||
 | 
			
		||||
/** @Annotation */
 | 
			
		||||
class Route
 | 
			
		||||
{
 | 
			
		||||
    /** @var string @Required */
 | 
			
		||||
    public $pattern;
 | 
			
		||||
    public $name;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,20 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Annotation;
 | 
			
		||||
 | 
			
		||||
/** @Annotation */
 | 
			
		||||
class Secure
 | 
			
		||||
{
 | 
			
		||||
    private $roles;
 | 
			
		||||
 | 
			
		||||
    public function __construct(array $values)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_string($values['value'])) {
 | 
			
		||||
            $values['value'] = array($values['value']);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->roles = $values['value'];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,16 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Annotation;
 | 
			
		||||
 | 
			
		||||
/** @Annotation */
 | 
			
		||||
class Template
 | 
			
		||||
{
 | 
			
		||||
    private $name;
 | 
			
		||||
 | 
			
		||||
    public function __construct(array $values)
 | 
			
		||||
    {
 | 
			
		||||
        $this->name = isset($values['value']) ? $values['value'] : null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,13 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Annotation;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target("PROPERTY")
 | 
			
		||||
 */
 | 
			
		||||
final class Version
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,23 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target("ALL")
 | 
			
		||||
 */
 | 
			
		||||
final class AnnotationEnum
 | 
			
		||||
{
 | 
			
		||||
    const ONE   = 'ONE';
 | 
			
		||||
    const TWO   = 'TWO';
 | 
			
		||||
    const THREE = 'THREE';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var mixed
 | 
			
		||||
     *
 | 
			
		||||
     * @Enum({"ONE","TWO","THREE"})
 | 
			
		||||
     */
 | 
			
		||||
    public $value;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,19 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target("ALL")
 | 
			
		||||
 */
 | 
			
		||||
final class AnnotationEnumInvalid
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * @var mixed
 | 
			
		||||
     *
 | 
			
		||||
     * @Enum({1, 2, "foo", "bar", {"foo":"bar"}})
 | 
			
		||||
     */
 | 
			
		||||
    public $value;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,36 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
use Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationEnumLiteral as SelfEnum;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target("ALL")
 | 
			
		||||
 */
 | 
			
		||||
final class AnnotationEnumLiteral
 | 
			
		||||
{
 | 
			
		||||
    const ONE   = 1;
 | 
			
		||||
    const TWO   = 2;
 | 
			
		||||
    const THREE = 3;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var mixed
 | 
			
		||||
     *
 | 
			
		||||
     * @Enum(
 | 
			
		||||
     *      value = {
 | 
			
		||||
     *          1,
 | 
			
		||||
     *          2,
 | 
			
		||||
     *          3,
 | 
			
		||||
     *      },
 | 
			
		||||
     *      literal = {
 | 
			
		||||
     *          1 : "AnnotationEnumLiteral::ONE",
 | 
			
		||||
     *          2 : "AnnotationEnumLiteral::TWO",
 | 
			
		||||
     *          3 : "AnnotationEnumLiteral::THREE",
 | 
			
		||||
     *      }
 | 
			
		||||
     * )
 | 
			
		||||
     */
 | 
			
		||||
    public $value;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,33 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target("ALL")
 | 
			
		||||
 */
 | 
			
		||||
final class AnnotationEnumLiteralInvalid
 | 
			
		||||
{
 | 
			
		||||
    const ONE   = 1;
 | 
			
		||||
    const TWO   = 2;
 | 
			
		||||
    const THREE = 3;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var mixed
 | 
			
		||||
     *
 | 
			
		||||
     * @Enum(
 | 
			
		||||
     *      value = {
 | 
			
		||||
     *          1,
 | 
			
		||||
     *          2
 | 
			
		||||
     *      },
 | 
			
		||||
     *      literal = {
 | 
			
		||||
     *          1 : "AnnotationEnumLiteral::ONE",
 | 
			
		||||
     *          2 : "AnnotationEnumLiteral::TWO",
 | 
			
		||||
     *          3 : "AnnotationEnumLiteral::THREE"
 | 
			
		||||
     *      }
 | 
			
		||||
     * )
 | 
			
		||||
     */
 | 
			
		||||
    public $value;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,16 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target("ALL")
 | 
			
		||||
 */
 | 
			
		||||
class AnnotationTargetAll
 | 
			
		||||
{
 | 
			
		||||
    public $data;
 | 
			
		||||
    public $name;
 | 
			
		||||
    public $target;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,16 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target({ "ANNOTATION" })
 | 
			
		||||
 */
 | 
			
		||||
final class AnnotationTargetAnnotation
 | 
			
		||||
{
 | 
			
		||||
    public $data;
 | 
			
		||||
    public $name;
 | 
			
		||||
    public $target;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,17 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target("CLASS")
 | 
			
		||||
 */
 | 
			
		||||
final class AnnotationTargetClass
 | 
			
		||||
{
 | 
			
		||||
    public $data;
 | 
			
		||||
    public $name;
 | 
			
		||||
    public $target;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,16 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target({ "METHOD", "PROPERTY" })
 | 
			
		||||
 */
 | 
			
		||||
final class AnnotationTargetPropertyMethod
 | 
			
		||||
{
 | 
			
		||||
    public $data;
 | 
			
		||||
    public $name;
 | 
			
		||||
    public $target;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,131 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target("ALL")
 | 
			
		||||
 * @Attributes({
 | 
			
		||||
      @Attribute("mixed",                type = "mixed"),
 | 
			
		||||
      @Attribute("boolean",              type = "boolean"),
 | 
			
		||||
      @Attribute("bool",                 type = "bool"),
 | 
			
		||||
      @Attribute("float",                type = "float"),
 | 
			
		||||
      @Attribute("string",               type = "string"),
 | 
			
		||||
      @Attribute("integer",              type = "integer"),
 | 
			
		||||
      @Attribute("array",                type = "array"),
 | 
			
		||||
      @Attribute("arrayOfIntegers",      type = "array<integer>"),
 | 
			
		||||
      @Attribute("arrayOfStrings",       type = "string[]"),
 | 
			
		||||
      @Attribute("annotation",           type = "Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetAll"),
 | 
			
		||||
      @Attribute("arrayOfAnnotations",   type = "array<Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetAll>"),
 | 
			
		||||
  })
 | 
			
		||||
 */
 | 
			
		||||
final class AnnotationWithAttributes
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public final function __construct(array $data)
 | 
			
		||||
    {
 | 
			
		||||
        foreach ($data as $key => $value) {
 | 
			
		||||
            $this->$key = $value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private $mixed;
 | 
			
		||||
    private $boolean;
 | 
			
		||||
    private $bool;
 | 
			
		||||
    private $float;
 | 
			
		||||
    private $string;
 | 
			
		||||
    private $integer;
 | 
			
		||||
    private $array;
 | 
			
		||||
    private $annotation;
 | 
			
		||||
    private $arrayOfIntegers;
 | 
			
		||||
    private $arrayOfStrings;
 | 
			
		||||
    private $arrayOfAnnotations;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return mixed
 | 
			
		||||
     */
 | 
			
		||||
    public function getMixed()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->mixed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return boolean
 | 
			
		||||
     */
 | 
			
		||||
    public function getBoolean()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->boolean;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return bool
 | 
			
		||||
     */
 | 
			
		||||
    public function getBool()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->bool;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return float
 | 
			
		||||
     */
 | 
			
		||||
    public function getFloat()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->float;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getString()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->string;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getInteger()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->integer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getArray()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->array;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetAll
 | 
			
		||||
     */
 | 
			
		||||
    public function getAnnotation()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->annotation;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string[]
 | 
			
		||||
     */
 | 
			
		||||
    public function getArrayOfStrings()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->arrayOfIntegers;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array<integer>
 | 
			
		||||
     */
 | 
			
		||||
    public function getArrayOfIntegers()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->arrayOfIntegers;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return array<Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetAll>
 | 
			
		||||
     */
 | 
			
		||||
    public function getArrayOfAnnotations()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->arrayOfAnnotations;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,22 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target("ALL")
 | 
			
		||||
 */
 | 
			
		||||
final class AnnotationWithConstants
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    const INTEGER = 1;
 | 
			
		||||
    const FLOAT   = 1.2;
 | 
			
		||||
    const STRING  = '1.2.3';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var mixed
 | 
			
		||||
     */
 | 
			
		||||
    public $value;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,52 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target("ALL")
 | 
			
		||||
 * @Attributes({
 | 
			
		||||
      @Attribute("value",   required = true ,   type = "string"),
 | 
			
		||||
      @Attribute("annot",   required = true ,   type = "Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetAnnotation"),
 | 
			
		||||
   })
 | 
			
		||||
 */
 | 
			
		||||
final class AnnotationWithRequiredAttributes
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public final function __construct(array $data)
 | 
			
		||||
    {
 | 
			
		||||
        foreach ($data as $key => $value) {
 | 
			
		||||
            $this->$key = $value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    private $value;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     *
 | 
			
		||||
     * @var Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetAnnotation
 | 
			
		||||
     */
 | 
			
		||||
    private $annot;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function getValue()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetAnnotation
 | 
			
		||||
     */
 | 
			
		||||
    public function getAnnot()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->annot;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,26 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target("ALL")
 | 
			
		||||
 */
 | 
			
		||||
final class AnnotationWithRequiredAttributesWithoutContructor
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @Required
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    public $value;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @Required
 | 
			
		||||
     * @var Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetAnnotation
 | 
			
		||||
     */
 | 
			
		||||
    public $annot;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,13 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target(@)
 | 
			
		||||
 */
 | 
			
		||||
final class AnnotationWithTargetSyntaxError
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,69 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 * @Target("ALL")
 | 
			
		||||
 */
 | 
			
		||||
final class AnnotationWithVarType
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var mixed
 | 
			
		||||
     */
 | 
			
		||||
    public $mixed;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var boolean
 | 
			
		||||
     */
 | 
			
		||||
    public $boolean;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var bool
 | 
			
		||||
     */
 | 
			
		||||
    public $bool;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var float
 | 
			
		||||
     */
 | 
			
		||||
    public $float;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    public $string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var integer
 | 
			
		||||
     */
 | 
			
		||||
    public $integer;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array
 | 
			
		||||
     */
 | 
			
		||||
    public $array;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetAll
 | 
			
		||||
     */
 | 
			
		||||
    public $annotation;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array<integer>
 | 
			
		||||
     */
 | 
			
		||||
    public $arrayOfIntegers;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string[]
 | 
			
		||||
     */
 | 
			
		||||
    public $arrayOfStrings;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array<Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetAll>
 | 
			
		||||
     */
 | 
			
		||||
    public $arrayOfAnnotations;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Attribute;
 | 
			
		||||
 | 
			
		||||
#[\Attribute]
 | 
			
		||||
final class AttributeClass
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Attribute;
 | 
			
		||||
 | 
			
		||||
#[/* Comment */\Drupal\Tests\Component\Annotation\Doctrine\Fixtures\ExtraAttributes\ExampleAttribute]
 | 
			
		||||
final class FullyQualified
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Attribute;
 | 
			
		||||
 | 
			
		||||
#[\Attribute /* Comment */]
 | 
			
		||||
#[/* Comment */AttributeClass]
 | 
			
		||||
final class MultipleAttributes
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Attribute;
 | 
			
		||||
 | 
			
		||||
// @phpstan-ignore attribute.notFound
 | 
			
		||||
#[NonexistentAttribute]
 | 
			
		||||
final class Nonexistent
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Attribute;
 | 
			
		||||
 | 
			
		||||
use Drupal\Tests\Component\Annotation\Doctrine\Fixtures\ExtraAttributes;
 | 
			
		||||
#[/* Comment */ExtraAttributes\ExampleAttribute]
 | 
			
		||||
final class Qualified
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Attribute;
 | 
			
		||||
 | 
			
		||||
#[/* Comment */SubDir\SubDirAttribute]
 | 
			
		||||
final class Relative
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Attribute\SubDir;
 | 
			
		||||
 | 
			
		||||
#[\Attribute]
 | 
			
		||||
final class SubDirAttribute
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,10 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Attribute;
 | 
			
		||||
 | 
			
		||||
use Drupal\Tests\Component\Annotation\Doctrine\Fixtures\ExtraAttributes\ExampleAttribute;
 | 
			
		||||
 | 
			
		||||
#[/* Comment */ExampleAttribute]
 | 
			
		||||
final class Used
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,10 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Attribute;
 | 
			
		||||
 | 
			
		||||
use Drupal\Tests\Component\Annotation\Doctrine\Fixtures\ExtraAttributes\ExampleAttribute as ClassAttribute;
 | 
			
		||||
 | 
			
		||||
#[/* Comment */ClassAttribute]
 | 
			
		||||
final class UsedAs
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,10 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\Attribute;
 | 
			
		||||
 | 
			
		||||
use Drupal\Tests\Component\Annotation\Doctrine\Fixtures\ExtraAttributes as ClassAttributes;
 | 
			
		||||
 | 
			
		||||
#[/* Comment */ClassAttributes\ExampleAttribute]
 | 
			
		||||
final class UsedAsQualified
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,13 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
class ClassWithConstants
 | 
			
		||||
{
 | 
			
		||||
    const SOME_VALUE  = 'ClassWithConstants.SOME_VALUE';
 | 
			
		||||
    const SOME_KEY    = 'ClassWithConstants.SOME_KEY';
 | 
			
		||||
    const OTHER_KEY_  = 'ClassWithConstants.OTHER_KEY_';
 | 
			
		||||
    const OTHER_KEY_2 = 'ClassWithConstants.OTHER_KEY_2';
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,19 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
use Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetPropertyMethod;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @AnnotationTargetPropertyMethod("Some data")
 | 
			
		||||
 */
 | 
			
		||||
class ClassWithInvalidAnnotationTargetAtClass
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @AnnotationTargetPropertyMethod("Bar")
 | 
			
		||||
     */
 | 
			
		||||
    public $foo;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,22 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
use Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetClass;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @AnnotationTargetClass("Some data")
 | 
			
		||||
 */
 | 
			
		||||
class ClassWithInvalidAnnotationTargetAtMethod
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @AnnotationTargetClass("functionName")
 | 
			
		||||
     */
 | 
			
		||||
    public function functionName($param)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,26 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
use Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetClass;
 | 
			
		||||
use Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetAnnotation;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @AnnotationTargetClass("Some data")
 | 
			
		||||
 */
 | 
			
		||||
class ClassWithInvalidAnnotationTargetAtProperty
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @AnnotationTargetClass("Bar")
 | 
			
		||||
     */
 | 
			
		||||
    public $foo;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @AnnotationTargetAnnotation("Foo")
 | 
			
		||||
     */
 | 
			
		||||
    public $bar;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,42 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
use Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetClass;
 | 
			
		||||
use Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetAll;
 | 
			
		||||
use Drupal\Tests\Component\Annotation\Doctrine\Fixtures\AnnotationTargetPropertyMethod;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @AnnotationTargetClass("Some data")
 | 
			
		||||
 */
 | 
			
		||||
class ClassWithValidAnnotationTarget
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @AnnotationTargetPropertyMethod("Some data")
 | 
			
		||||
     */
 | 
			
		||||
    public $foo;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @AnnotationTargetAll("Some data",name="Some name")
 | 
			
		||||
     */
 | 
			
		||||
    public $name;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @AnnotationTargetPropertyMethod("Some data",name="Some name")
 | 
			
		||||
     */
 | 
			
		||||
    public function someFunction()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @AnnotationTargetAll(@AnnotationTargetAnnotation)
 | 
			
		||||
     */
 | 
			
		||||
    public $nested;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\ExtraAttributes;
 | 
			
		||||
 | 
			
		||||
#[\Attribute]
 | 
			
		||||
final class ExampleAttribute extends ExampleParentAttribute
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures\ExtraAttributes;
 | 
			
		||||
 | 
			
		||||
#[\Attribute]
 | 
			
		||||
class ExampleParentAttribute {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,12 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Fixtures;
 | 
			
		||||
 | 
			
		||||
interface IntefaceWithConstants
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    const SOME_VALUE = 'IntefaceWithConstants.SOME_VALUE';
 | 
			
		||||
    const SOME_KEY   = 'IntefaceWithConstants.SOME_KEY';
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,43 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Annotation\Doctrine\StaticReflectionParser;
 | 
			
		||||
use Drupal\Component\Annotation\Reflection\MockFileFinder;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Annotation\Doctrine\StaticReflectionParser
 | 
			
		||||
 *
 | 
			
		||||
 * @group Annotation
 | 
			
		||||
 */
 | 
			
		||||
class StaticReflectionParserTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @testWith ["AttributeClass", "\\Attribute", true]
 | 
			
		||||
   *           ["AttributeClass", "attribute", true]
 | 
			
		||||
   *           ["AttributeClass", "Attribute", true]
 | 
			
		||||
   *           ["AttributeClass", "\\DoesNotExist", false]
 | 
			
		||||
   *           ["Nonexistent", "NonexistentAttribute", false]
 | 
			
		||||
   *           ["MultipleAttributes", "Attribute", true]
 | 
			
		||||
   *           ["MultipleAttributes", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\Attribute\\AttributeClass", true]
 | 
			
		||||
   *           ["MultipleAttributes", "DoesNotExist", false]
 | 
			
		||||
   *           ["FullyQualified", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleAttribute", true]
 | 
			
		||||
   *           ["Used", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleAttribute", true]
 | 
			
		||||
   *           ["UsedAs", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleAttribute", true]
 | 
			
		||||
   *           ["UsedAsQualified", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleAttribute", true]
 | 
			
		||||
   *           ["Qualified", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleAttribute", true]
 | 
			
		||||
   *           ["Relative", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\Attribute\\SubDir\\SubDirAttribute", true]
 | 
			
		||||
   *           ["FullyQualified", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleParentAttribute", true]
 | 
			
		||||
   *           ["Used", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleParentAttribute", true]
 | 
			
		||||
   *           ["UsedAs", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleParentAttribute", true]
 | 
			
		||||
   *           ["UsedAsQualified", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleParentAttribute", true]
 | 
			
		||||
   *           ["Qualified", "Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\ExtraAttributes\\ExampleParentAttribute", true]
 | 
			
		||||
   */
 | 
			
		||||
  public function testAttribute(string $class, string $attribute_class, bool $expected): void {
 | 
			
		||||
    $finder = MockFileFinder::create(__DIR__ . '/Fixtures/Attribute/' . $class . '.php');
 | 
			
		||||
    $parser = new StaticReflectionParser('\\Drupal\\Tests\\Component\\Annotation\\Doctrine\\Fixtures\\Attribute\\' . $class, $finder);
 | 
			
		||||
    $this->assertSame($expected, $parser->hasClassAttribute($attribute_class), "'$class' has attribute that is a '$attribute_class'");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,16 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
// Some class named Entity in the global namespace
 | 
			
		||||
/**
 | 
			
		||||
 * This class is a near-copy of
 | 
			
		||||
 * tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Entity.php, which is
 | 
			
		||||
 * part of the Doctrine project: <http://www.doctrine-project.org>.  It was
 | 
			
		||||
 * copied from version 1.2.7.
 | 
			
		||||
 *
 | 
			
		||||
 * @Annotation
 | 
			
		||||
 */
 | 
			
		||||
class Entity
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,119 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Ticket;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Annotation\Doctrine\DocParser;
 | 
			
		||||
use Drupal\Component\Annotation\Doctrine\SimpleAnnotationReader;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class is a near-copy of
 | 
			
		||||
 * \Doctrine\Tests\Common\Annotations\Ticket\DCOM58Test, which is part of the
 | 
			
		||||
 * Doctrine project: <http://www.doctrine-project.org>.  It was copied from
 | 
			
		||||
 * version 1.2.7.
 | 
			
		||||
 *
 | 
			
		||||
 * Run this test in a separate process as it includes code that might have side
 | 
			
		||||
 * effects.
 | 
			
		||||
 *
 | 
			
		||||
 * @group DCOM58
 | 
			
		||||
 * @runTestsInSeparateProcesses
 | 
			
		||||
 */
 | 
			
		||||
class DCOM58Test extends TestCase
 | 
			
		||||
{
 | 
			
		||||
    protected function setUp(): void
 | 
			
		||||
    {
 | 
			
		||||
        // Some class named Entity in the global namespace.
 | 
			
		||||
        include __DIR__ .'/DCOM58Entity.php';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIssueGlobalNamespace(): void
 | 
			
		||||
    {
 | 
			
		||||
        $docblock   = "@Entity";
 | 
			
		||||
        $parser     = new DocParser();
 | 
			
		||||
        $parser->setImports(array(
 | 
			
		||||
            "__NAMESPACE__" =>"Drupal\Tests\Component\Annotation\Doctrine\Ticket\Doctrine\ORM\Mapping"
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        $annots     = $parser->parse($docblock);
 | 
			
		||||
 | 
			
		||||
        $this->assertCount(1, $annots);
 | 
			
		||||
        $this->assertInstanceOf("Drupal\Tests\Component\Annotation\Doctrine\Ticket\Doctrine\ORM\Mapping\Entity", $annots[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIssueNamespaces(): void
 | 
			
		||||
    {
 | 
			
		||||
        $docblock   = "@Entity";
 | 
			
		||||
        $parser     = new DocParser();
 | 
			
		||||
        $parser->addNamespace("Drupal\Tests\Component\Annotation\Doctrine\Ticket\Doctrine\ORM");
 | 
			
		||||
 | 
			
		||||
        $annots     = $parser->parse($docblock);
 | 
			
		||||
 | 
			
		||||
        $this->assertCount(1, $annots);
 | 
			
		||||
        $this->assertInstanceOf("Drupal\Tests\Component\Annotation\Doctrine\Ticket\Doctrine\ORM\Entity", $annots[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIssueMultipleNamespaces(): void
 | 
			
		||||
    {
 | 
			
		||||
        $docblock   = "@Entity";
 | 
			
		||||
        $parser     = new DocParser();
 | 
			
		||||
        $parser->addNamespace("Drupal\Tests\Component\Annotation\Doctrine\Ticket\Doctrine\ORM\Mapping");
 | 
			
		||||
        $parser->addNamespace("Drupal\Tests\Component\Annotation\Doctrine\Ticket\Doctrine\ORM");
 | 
			
		||||
 | 
			
		||||
        $annots     = $parser->parse($docblock);
 | 
			
		||||
 | 
			
		||||
        $this->assertCount(1, $annots);
 | 
			
		||||
        $this->assertInstanceOf("Drupal\Tests\Component\Annotation\Doctrine\Ticket\Doctrine\ORM\Mapping\Entity", $annots[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIssueWithNamespacesOrImports(): void
 | 
			
		||||
    {
 | 
			
		||||
        $docblock   = "@Entity";
 | 
			
		||||
        $parser     = new DocParser();
 | 
			
		||||
        $annots     = $parser->parse($docblock);
 | 
			
		||||
 | 
			
		||||
        $this->assertCount(1, $annots);
 | 
			
		||||
        $this->assertInstanceOf("Entity", $annots[0]);
 | 
			
		||||
        $this->assertCount(1, $annots);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function testIssueSimpleAnnotationReader(): void
 | 
			
		||||
    {
 | 
			
		||||
        $reader     = new SimpleAnnotationReader();
 | 
			
		||||
        $reader->addNamespace('Drupal\Tests\Component\Annotation\Doctrine\Ticket\Doctrine\ORM\Mapping');
 | 
			
		||||
        $annots     = $reader->getClassAnnotations(new \ReflectionClass(__NAMESPACE__."\MappedClass"));
 | 
			
		||||
 | 
			
		||||
        $this->assertCount(1, $annots);
 | 
			
		||||
        $this->assertInstanceOf("Drupal\Tests\Component\Annotation\Doctrine\Ticket\Doctrine\ORM\Mapping\Entity", $annots[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @Entity
 | 
			
		||||
 */
 | 
			
		||||
class MappedClass
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Ticket\Doctrine\ORM\Mapping;
 | 
			
		||||
/**
 | 
			
		||||
* @Annotation
 | 
			
		||||
*/
 | 
			
		||||
class Entity
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Doctrine\Ticket\Doctrine\ORM;
 | 
			
		||||
/**
 | 
			
		||||
* @Annotation
 | 
			
		||||
*/
 | 
			
		||||
class Entity
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,16 @@
 | 
			
		||||
All files within this directory where copied from the  Doctrine project: <http://www.doctrine-project.org>
 | 
			
		||||
They were copied from version 1.2.7.
 | 
			
		||||
 | 
			
		||||
Original copyright:
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2006-2013 Doctrine Project
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
 | 
			
		||||
this software and associated documentation files (the "Software"), to deal in
 | 
			
		||||
the Software without restriction, including without limitation the rights to
 | 
			
		||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 | 
			
		||||
of the Software, and to permit persons to whom the Software is furnished to do
 | 
			
		||||
so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
@ -0,0 +1,18 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace com\example\PluginNamespace;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Provides a custom test plugin.
 | 
			
		||||
 *
 | 
			
		||||
 * @Plugin(
 | 
			
		||||
 *   id = "discovery_test_1"
 | 
			
		||||
 * )
 | 
			
		||||
 * @CustomPlugin(
 | 
			
		||||
 *   id = "discovery_test_1",
 | 
			
		||||
 *   title = "Discovery test plugin"
 | 
			
		||||
 * )
 | 
			
		||||
 */
 | 
			
		||||
class DiscoveryTest1 {}
 | 
			
		||||
@ -0,0 +1,2 @@
 | 
			
		||||
# This should not be loaded by our annotated class discovery.
 | 
			
		||||
id:discovery_test_2
 | 
			
		||||
@ -0,0 +1,26 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Annotation\Reflection\MockFileFinder;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Annotation\Reflection\MockFileFinder
 | 
			
		||||
 * @group Annotation
 | 
			
		||||
 */
 | 
			
		||||
class MockFileFinderTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::create
 | 
			
		||||
   * @covers ::findFile
 | 
			
		||||
   */
 | 
			
		||||
  public function testFindFile(): void {
 | 
			
		||||
    $tmp = MockFileFinder::create('test_filename.txt');
 | 
			
		||||
    $this->assertEquals('test_filename.txt', $tmp->findFile('n/a'));
 | 
			
		||||
    $this->assertEquals('test_filename.txt', $tmp->findFile('SomeClass'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,74 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation\Plugin\Discovery;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Annotation\Plugin;
 | 
			
		||||
use Drupal\Component\Annotation\Plugin\Discovery\AnnotationBridgeDecorator;
 | 
			
		||||
use Drupal\Component\Plugin\Definition\PluginDefinition;
 | 
			
		||||
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Prophecy\PhpUnit\ProphecyTrait;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Annotation\Plugin\Discovery\AnnotationBridgeDecorator
 | 
			
		||||
 * @group Plugin
 | 
			
		||||
 */
 | 
			
		||||
class AnnotationBridgeDecoratorTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  use ProphecyTrait;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getDefinitions
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetDefinitions(): void {
 | 
			
		||||
    $definitions = [];
 | 
			
		||||
    $definitions['object'] = new ObjectDefinition(['id' => 'foo']);
 | 
			
		||||
    $definitions['array'] = ['id' => 'bar'];
 | 
			
		||||
    $discovery = $this->prophesize(DiscoveryInterface::class);
 | 
			
		||||
    $discovery->getDefinitions()->willReturn($definitions);
 | 
			
		||||
 | 
			
		||||
    $decorator = new AnnotationBridgeDecorator($discovery->reveal(), TestAnnotation::class);
 | 
			
		||||
 | 
			
		||||
    $expected = [
 | 
			
		||||
      'object' => new ObjectDefinition(['id' => 'foo']),
 | 
			
		||||
      'array' => new ObjectDefinition(['id' => 'bar']),
 | 
			
		||||
    ];
 | 
			
		||||
    $this->assertEquals($expected, $decorator->getDefinitions());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * {@inheritdoc}
 | 
			
		||||
 */
 | 
			
		||||
class TestAnnotation extends Plugin {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function get() {
 | 
			
		||||
    return new ObjectDefinition($this->definition);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * {@inheritdoc}
 | 
			
		||||
 */
 | 
			
		||||
class ObjectDefinition extends PluginDefinition {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * ObjectDefinition constructor.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $definition
 | 
			
		||||
   *   An array of definition values.
 | 
			
		||||
   */
 | 
			
		||||
  public function __construct(array $definition) {
 | 
			
		||||
    foreach ($definition as $property => $value) {
 | 
			
		||||
      $this->{$property} = $value;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,48 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Annotation\PluginID;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Annotation\PluginID
 | 
			
		||||
 * @group Annotation
 | 
			
		||||
 */
 | 
			
		||||
class PluginIdTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::get
 | 
			
		||||
   */
 | 
			
		||||
  public function testGet(): void {
 | 
			
		||||
    // Assert plugin starts empty.
 | 
			
		||||
    $plugin = new PluginID();
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'id' => NULL,
 | 
			
		||||
      'class' => NULL,
 | 
			
		||||
      'provider' => NULL,
 | 
			
		||||
    ], $plugin->get());
 | 
			
		||||
 | 
			
		||||
    // Set values and ensure we can retrieve them.
 | 
			
		||||
    $plugin->value = 'foo';
 | 
			
		||||
    $plugin->setClass('bar');
 | 
			
		||||
    $plugin->setProvider('baz');
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'id' => 'foo',
 | 
			
		||||
      'class' => 'bar',
 | 
			
		||||
      'provider' => 'baz',
 | 
			
		||||
    ], $plugin->get());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getId
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetId(): void {
 | 
			
		||||
    $plugin = new PluginID();
 | 
			
		||||
    $plugin->value = 'example';
 | 
			
		||||
    $this->assertEquals('example', $plugin->getId());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										108
									
								
								web/core/tests/Drupal/Tests/Component/Annotation/PluginTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								web/core/tests/Drupal/Tests/Component/Annotation/PluginTest.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,108 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Annotation;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Annotation\Plugin;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Annotation\Plugin
 | 
			
		||||
 * @group Annotation
 | 
			
		||||
 */
 | 
			
		||||
class PluginTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::__construct
 | 
			
		||||
   * @covers ::parse
 | 
			
		||||
   * @covers ::get
 | 
			
		||||
   */
 | 
			
		||||
  public function testGet(): void {
 | 
			
		||||
    // Assert all values are accepted through constructor and default value is
 | 
			
		||||
    // used for non existent but defined property.
 | 
			
		||||
    $plugin = new PluginStub([
 | 
			
		||||
      1 => 'oak',
 | 
			
		||||
      'foo' => 'bar',
 | 
			
		||||
      'biz' => [
 | 
			
		||||
        'baz' => 'boom',
 | 
			
		||||
      ],
 | 
			
		||||
      'nestedAnnotation' => new Plugin([
 | 
			
		||||
        'foo' => 'bar',
 | 
			
		||||
      ]),
 | 
			
		||||
    ]);
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      // This property wasn't in our definition but is defined as a property on
 | 
			
		||||
      // our plugin class.
 | 
			
		||||
      'defaultProperty' => 'test_value',
 | 
			
		||||
      1 => 'oak',
 | 
			
		||||
      'foo' => 'bar',
 | 
			
		||||
      'biz' => [
 | 
			
		||||
        'baz' => 'boom',
 | 
			
		||||
      ],
 | 
			
		||||
      'nestedAnnotation' => [
 | 
			
		||||
        'foo' => 'bar',
 | 
			
		||||
      ],
 | 
			
		||||
    ], $plugin->get());
 | 
			
		||||
 | 
			
		||||
    // Without default properties, we get a completely empty plugin definition.
 | 
			
		||||
    $plugin = new Plugin([]);
 | 
			
		||||
    $this->assertEquals([], $plugin->get());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getProvider
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetProvider(): void {
 | 
			
		||||
    $plugin = new Plugin(['provider' => 'example']);
 | 
			
		||||
    $this->assertEquals('example', $plugin->getProvider());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::setProvider
 | 
			
		||||
   */
 | 
			
		||||
  public function testSetProvider(): void {
 | 
			
		||||
    $plugin = new Plugin([]);
 | 
			
		||||
    $plugin->setProvider('example');
 | 
			
		||||
    $this->assertEquals('example', $plugin->getProvider());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getId
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetId(): void {
 | 
			
		||||
    $plugin = new Plugin(['id' => 'example']);
 | 
			
		||||
    $this->assertEquals('example', $plugin->getId());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetClass(): void {
 | 
			
		||||
    $plugin = new Plugin(['class' => 'example']);
 | 
			
		||||
    $this->assertEquals('example', $plugin->getClass());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::setClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testSetClass(): void {
 | 
			
		||||
    $plugin = new Plugin([]);
 | 
			
		||||
    $plugin->setClass('example');
 | 
			
		||||
    $this->assertEquals('example', $plugin->getClass());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * {@inheritdoc}
 | 
			
		||||
 */
 | 
			
		||||
class PluginStub extends Plugin {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * A default property for testing.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   */
 | 
			
		||||
  protected $defaultProperty = 'test_value';
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,255 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Assertion;
 | 
			
		||||
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Drupal\Component\Assertion\Inspector;
 | 
			
		||||
use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Assertion\Inspector
 | 
			
		||||
 * @group Assertion
 | 
			
		||||
 */
 | 
			
		||||
class InspectorTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  use ExpectDeprecationTrait;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting all members are strings.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertAllStrings
 | 
			
		||||
   * @dataProvider providerTestAssertAllStrings
 | 
			
		||||
   */
 | 
			
		||||
  public function testAssertAllStrings($input, $expected): void {
 | 
			
		||||
    $this->assertSame($expected, Inspector::assertAllStrings($input));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static function providerTestAssertAllStrings() {
 | 
			
		||||
    $data = [
 | 
			
		||||
      'empty-array' => [[], TRUE],
 | 
			
		||||
      'array-with-strings' => [['foo', 'bar'], TRUE],
 | 
			
		||||
      'string' => ['foo', FALSE],
 | 
			
		||||
      'array-with-strings-with-colon' => [['foo', 'bar', 'llama:2001988', 'baz', 'llama:14031991'], TRUE],
 | 
			
		||||
 | 
			
		||||
      'with-FALSE' => [[FALSE], FALSE],
 | 
			
		||||
      'with-TRUE' => [[TRUE], FALSE],
 | 
			
		||||
      'with-string-and-boolean' => [['foo', FALSE], FALSE],
 | 
			
		||||
      'with-NULL' => [[NULL], FALSE],
 | 
			
		||||
      'string-with-NULL' => [['foo', NULL], FALSE],
 | 
			
		||||
      'integer' => [[1337], FALSE],
 | 
			
		||||
      'string-and-integer' => [['foo', 1337], FALSE],
 | 
			
		||||
      'double' => [[3.14], FALSE],
 | 
			
		||||
      'string-and-double' => [['foo', 3.14], FALSE],
 | 
			
		||||
      'array' => [[[]], FALSE],
 | 
			
		||||
      'string-and-array' => [['foo', []], FALSE],
 | 
			
		||||
      'string-and-nested-array' => [['foo', ['bar']], FALSE],
 | 
			
		||||
      'object' => [[new \stdClass()], FALSE],
 | 
			
		||||
      'string-and-object' => [['foo', new StringObject()], FALSE],
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    return $data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting all members are strings or objects with __toString().
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertAllStringable
 | 
			
		||||
   */
 | 
			
		||||
  public function testAssertAllStringable(): void {
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllStringable([]));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllStringable(['foo', 'bar']));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllStringable('foo'));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllStringable(['foo', new StringObject()]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting all members are arrays.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertAllArrays
 | 
			
		||||
   */
 | 
			
		||||
  public function testAssertAllArrays(): void {
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllArrays([]));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllArrays([[], []]));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllArrays([[], 'foo']));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting array is 0-indexed - the strict definition of array.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertStrictArray
 | 
			
		||||
   */
 | 
			
		||||
  public function testAssertStrictArray(): void {
 | 
			
		||||
    $this->assertTrue(Inspector::assertStrictArray([]));
 | 
			
		||||
    $this->assertTrue(Inspector::assertStrictArray(['bar', 'foo']));
 | 
			
		||||
    $this->assertFalse(Inspector::assertStrictArray(['foo' => 'bar', 'bar' => 'foo']));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting all members are strict arrays.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertAllStrictArrays
 | 
			
		||||
   */
 | 
			
		||||
  public function testAssertAllStrictArrays(): void {
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllStrictArrays([]));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllStrictArrays([[], []]));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllStrictArrays([['foo' => 'bar', 'bar' => 'foo']]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting all members have specified keys.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertAllHaveKey
 | 
			
		||||
   */
 | 
			
		||||
  public function testAssertAllHaveKey(): void {
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllHaveKey([]));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllHaveKey([['foo' => 'bar', 'bar' => 'foo']]));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllHaveKey([['foo' => 'bar', 'bar' => 'foo']], 'foo'));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllHaveKey([['foo' => 'bar', 'bar' => 'foo']], 'bar', 'foo'));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllHaveKey([['foo' => 'bar', 'bar' => 'foo']], 'bar', 'foo', 'moo'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting all members are integers.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertAllIntegers
 | 
			
		||||
   */
 | 
			
		||||
  public function testAssertAllIntegers(): void {
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllIntegers([]));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllIntegers([1, 2, 3]));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllIntegers([1, 2, 3.14]));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllIntegers([1, '2', 3]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting all members are floating point variables.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertAllFloat
 | 
			
		||||
   */
 | 
			
		||||
  public function testAssertAllFloat(): void {
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllFloat([]));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllFloat([1.0, 2.1, 3.14]));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllFloat([1, 2.1, 3.14]));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllFloat([1.0, '2', 3]));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllFloat(['Titanic']));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting all members are callable.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertAllCallable
 | 
			
		||||
   */
 | 
			
		||||
  public function testAllCallable(): void {
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllCallable([
 | 
			
		||||
      'strchr',
 | 
			
		||||
      [$this, 'callMe'],
 | 
			
		||||
      [__CLASS__, 'callMeStatic'],
 | 
			
		||||
      function () {
 | 
			
		||||
        return TRUE;
 | 
			
		||||
      },
 | 
			
		||||
    ]));
 | 
			
		||||
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllCallable([
 | 
			
		||||
      'strchr',
 | 
			
		||||
      [$this, 'callMe'],
 | 
			
		||||
      [__CLASS__, 'callMeStatic'],
 | 
			
		||||
      function () {
 | 
			
		||||
        return TRUE;
 | 
			
		||||
      },
 | 
			
		||||
      "I'm not callable",
 | 
			
		||||
    ]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting all members are !empty().
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertAllNotEmpty
 | 
			
		||||
   */
 | 
			
		||||
  public function testAllNotEmpty(): void {
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllNotEmpty([1, 'two']));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllNotEmpty(['']));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting all arguments are numbers or strings castable to numbers.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertAllNumeric
 | 
			
		||||
   */
 | 
			
		||||
  public function testAssertAllNumeric(): void {
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllNumeric([1, '2', 3.14]));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllNumeric([1, 'two', 3.14]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting strstr() or stristr() match.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertAllMatch
 | 
			
		||||
   */
 | 
			
		||||
  public function testAssertAllMatch(): void {
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllMatch('f', ['fee', 'fi', 'fo']));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllMatch('F', ['fee', 'fi', 'fo']));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllMatch('f', ['fee', 'fi', 'fo'], TRUE));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllMatch('F', ['fee', 'fi', 'fo'], TRUE));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllMatch('e', ['fee', 'fi', 'fo']));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllMatch('1', [12]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting regular expression match.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertAllRegularExpressionMatch
 | 
			
		||||
   */
 | 
			
		||||
  public function testAssertAllRegularExpressionMatch(): void {
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllRegularExpressionMatch('/f/i', ['fee', 'fi', 'fo']));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllRegularExpressionMatch('/F/i', ['fee', 'fi', 'fo']));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllRegularExpressionMatch('/f/', ['fee', 'fi', 'fo']));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllRegularExpressionMatch('/F/', ['fee', 'fi', 'fo']));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllRegularExpressionMatch('/e/', ['fee', 'fi', 'fo']));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllRegularExpressionMatch('/1/', [12]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests asserting all members are objects.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::assertAllObjects
 | 
			
		||||
   */
 | 
			
		||||
  public function testAssertAllObjects(): void {
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllObjects([new \ArrayObject(), new \ArrayObject()]));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllObjects([new \ArrayObject(), new \ArrayObject(), 'foo']));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllObjects([new \ArrayObject(), new \ArrayObject()], '\\Traversable'));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllObjects([new \ArrayObject(), new \ArrayObject(), 'foo'], '\\Traversable'));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllObjects([new \ArrayObject(), new StringObject()], '\\Traversable'));
 | 
			
		||||
    $this->assertTrue(Inspector::assertAllObjects([new \ArrayObject(), new StringObject()], '\\Traversable', '\\Drupal\\Tests\\Component\\Assertion\\StringObject'));
 | 
			
		||||
    $this->assertFalse(Inspector::assertAllObjects([new \ArrayObject(), new StringObject(), new \stdClass()], '\\ArrayObject', '\\Drupal\\Tests\\Component\\Assertion\\StringObject'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Defines a test method referenced by ::testAllCallable().
 | 
			
		||||
   */
 | 
			
		||||
  public function callMe() {
 | 
			
		||||
    return TRUE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Defines a test method referenced by ::testAllCallable().
 | 
			
		||||
   */
 | 
			
		||||
  public static function callMeStatic() {
 | 
			
		||||
    return TRUE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Quick class for testing for objects with __toString.
 | 
			
		||||
 */
 | 
			
		||||
class StringObject {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function __toString() {
 | 
			
		||||
    return 'foo';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,42 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\ClassFinder;
 | 
			
		||||
 | 
			
		||||
use Composer\Autoload\ClassLoader;
 | 
			
		||||
use Drupal\Component\ClassFinder\ClassFinder;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\ClassFinder\ClassFinder
 | 
			
		||||
 * @group ClassFinder
 | 
			
		||||
 */
 | 
			
		||||
class ClassFinderTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::findFile
 | 
			
		||||
   */
 | 
			
		||||
  public function testFindFile(): void {
 | 
			
		||||
    $finder = new ClassFinder();
 | 
			
		||||
 | 
			
		||||
    // The full path is returned therefore only tests with
 | 
			
		||||
    // assertStringEndsWith() so the test is portable.
 | 
			
		||||
    $expected_path = str_replace('/', DIRECTORY_SEPARATOR, 'core/tests/Drupal/Tests/Component/ClassFinder/ClassFinderTest.php');
 | 
			
		||||
    $this->assertStringEndsWith($expected_path, $finder->findFile(ClassFinderTest::class));
 | 
			
		||||
    $class = 'Not\\A\\Class';
 | 
			
		||||
    $this->assertNull($finder->findFile($class));
 | 
			
		||||
 | 
			
		||||
    // Register an autoloader that can find this class.
 | 
			
		||||
    $loader = new ClassLoader();
 | 
			
		||||
    $loader->addClassMap([$class => __FILE__]);
 | 
			
		||||
    $loader->register();
 | 
			
		||||
    $this->assertEquals(__FILE__, $finder->findFile($class));
 | 
			
		||||
    // This shouldn't prevent us from finding the original file.
 | 
			
		||||
    $this->assertStringEndsWith($expected_path, $finder->findFile(ClassFinderTest::class));
 | 
			
		||||
 | 
			
		||||
    // Clean up the additional autoloader after the test.
 | 
			
		||||
    $loader->unregister();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,944 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Datetime;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Datetime\DateTimePlus;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Datetime\DateTimePlus
 | 
			
		||||
 * @group Datetime
 | 
			
		||||
 */
 | 
			
		||||
class DateTimePlusTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests creating dates from string and array input.
 | 
			
		||||
   *
 | 
			
		||||
   * @param mixed $input
 | 
			
		||||
   *   Input argument for DateTimePlus.
 | 
			
		||||
   * @param string $timezone
 | 
			
		||||
   *   Timezone argument for DateTimePlus.
 | 
			
		||||
   * @param string $expected
 | 
			
		||||
   *   Expected output from DateTimePlus::format().
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestDates
 | 
			
		||||
   */
 | 
			
		||||
  public function testDates($input, $timezone, $expected): void {
 | 
			
		||||
    $date = new DateTimePlus($input, $timezone);
 | 
			
		||||
    $value = $date->format('c');
 | 
			
		||||
 | 
			
		||||
    if (is_array($input)) {
 | 
			
		||||
      $input = var_export($input, TRUE);
 | 
			
		||||
    }
 | 
			
		||||
    $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $timezone, $expected, $value));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests creating dates from string and array input.
 | 
			
		||||
   *
 | 
			
		||||
   * @param mixed $input
 | 
			
		||||
   *   Input argument for DateTimePlus.
 | 
			
		||||
   * @param string $timezone
 | 
			
		||||
   *   Timezone argument for DateTimePlus.
 | 
			
		||||
   * @param string $expected
 | 
			
		||||
   *   Expected output from DateTimePlus::format().
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestDateArrays
 | 
			
		||||
   */
 | 
			
		||||
  public function testDateArrays($input, $timezone, $expected): void {
 | 
			
		||||
    $date = DateTimePlus::createFromArray($input, $timezone);
 | 
			
		||||
    $value = $date->format('c');
 | 
			
		||||
 | 
			
		||||
    if (is_array($input)) {
 | 
			
		||||
      $input = var_export($input, TRUE);
 | 
			
		||||
    }
 | 
			
		||||
    $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $timezone, $expected, $value));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests date diffs.
 | 
			
		||||
   *
 | 
			
		||||
   * @param mixed $input1
 | 
			
		||||
   *   A DateTimePlus object.
 | 
			
		||||
   * @param mixed $input2
 | 
			
		||||
   *   Date argument for DateTimePlus::diff method.
 | 
			
		||||
   * @param bool $absolute
 | 
			
		||||
   *   Absolute flag for DateTimePlus::diff method.
 | 
			
		||||
   * @param \DateInterval $expected
 | 
			
		||||
   *   The expected result of the DateTimePlus::diff operation.
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestDateDiff
 | 
			
		||||
   */
 | 
			
		||||
  public function testDateDiff($input1, $input2, $absolute, \DateInterval $expected): void {
 | 
			
		||||
    $interval = $input1->diff($input2, $absolute);
 | 
			
		||||
    $this->assertEquals($interval, $expected);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests date diff exception caused by invalid input.
 | 
			
		||||
   *
 | 
			
		||||
   * @param mixed $input1
 | 
			
		||||
   *   A DateTimePlus object.
 | 
			
		||||
   * @param mixed $input2
 | 
			
		||||
   *   Date argument for DateTimePlus::diff method.
 | 
			
		||||
   * @param bool $absolute
 | 
			
		||||
   *   Absolute flag for DateTimePlus::diff method.
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestInvalidDateDiff
 | 
			
		||||
   */
 | 
			
		||||
  public function testInvalidDateDiff($input1, $input2, $absolute): void {
 | 
			
		||||
    $this->expectException(\BadMethodCallException::class);
 | 
			
		||||
    $this->expectExceptionMessage('Method Drupal\Component\Datetime\DateTimePlus::diff expects parameter 1 to be a \DateTime or \Drupal\Component\Datetime\DateTimePlus object');
 | 
			
		||||
    $input1->diff($input2, $absolute);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests creating dates from invalid array input.
 | 
			
		||||
   *
 | 
			
		||||
   * @param mixed $input
 | 
			
		||||
   *   Input argument for DateTimePlus.
 | 
			
		||||
   * @param string $timezone
 | 
			
		||||
   *   Timezone argument for DateTimePlus.
 | 
			
		||||
   * @param string $class
 | 
			
		||||
   *   The Exception subclass to expect to be thrown.
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestInvalidDateArrays
 | 
			
		||||
   */
 | 
			
		||||
  public function testInvalidDateArrays($input, $timezone, $class): void {
 | 
			
		||||
    $this->expectException($class);
 | 
			
		||||
    $this->assertInstanceOf(
 | 
			
		||||
      '\Drupal\Component\DateTimePlus',
 | 
			
		||||
      DateTimePlus::createFromArray($input, $timezone)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests DateTimePlus::checkArray().
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $array
 | 
			
		||||
   *   Input argument for DateTimePlus::checkArray().
 | 
			
		||||
   * @param bool $expected
 | 
			
		||||
   *   The expected result of DateTimePlus::checkArray().
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestCheckArray
 | 
			
		||||
   */
 | 
			
		||||
  public function testCheckArray(array $array, $expected): void {
 | 
			
		||||
    $this->assertSame(
 | 
			
		||||
      $expected,
 | 
			
		||||
      DateTimePlus::checkArray($array)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests creating dates from timestamps, and manipulating timezones.
 | 
			
		||||
   *
 | 
			
		||||
   * @param int $input
 | 
			
		||||
   *   Input argument for DateTimePlus::createFromTimestamp().
 | 
			
		||||
   * @param array $initial
 | 
			
		||||
   *   An array containing:
 | 
			
		||||
   *   - 'timezone_initial' - Timezone argument for DateTimePlus.
 | 
			
		||||
   *   - 'format_initial' - Format argument for DateTimePlus.
 | 
			
		||||
   *   - 'expected_initial_date' - Expected output from DateTimePlus::format().
 | 
			
		||||
   *   - 'expected_initial_timezone' - Expected output from
 | 
			
		||||
   *      DateTimePlus::getTimeZone()::getName().
 | 
			
		||||
   *   - 'expected_initial_offset' - Expected output from
 | 
			
		||||
   *      DateTimePlus::getOffset().
 | 
			
		||||
   * @param array $transform
 | 
			
		||||
   *   An array containing:
 | 
			
		||||
   *   - 'timezone_transform' - Argument to transform date to another timezone
 | 
			
		||||
   *     via DateTimePlus::setTimezone().
 | 
			
		||||
   *   - 'format_transform' - Format argument to use when transforming date to
 | 
			
		||||
   *     another timezone.
 | 
			
		||||
   *   - 'expected_transform_date' - Expected output from
 | 
			
		||||
   *     DateTimePlus::format(), after timezone transform.
 | 
			
		||||
   *   - 'expected_transform_timezone' - Expected output from
 | 
			
		||||
   *     DateTimePlus::getTimeZone()::getName(), after timezone transform.
 | 
			
		||||
   *   - 'expected_transform_offset' - Expected output from
 | 
			
		||||
   *      DateTimePlus::getOffset(), after timezone transform.
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestTimestamp
 | 
			
		||||
   */
 | 
			
		||||
  public function testTimestamp($input, array $initial, array $transform): void {
 | 
			
		||||
    // Initialize a new date object.
 | 
			
		||||
    $date = DateTimePlus::createFromTimestamp($input, $initial['timezone']);
 | 
			
		||||
    $this->assertDateTimestamp($date, $input, $initial, $transform);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests creating dates from datetime strings.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $input
 | 
			
		||||
   *   Input argument for DateTimePlus().
 | 
			
		||||
   * @param array $initial
 | 
			
		||||
   *   @see testTimestamp()
 | 
			
		||||
   * @param array $transform
 | 
			
		||||
   *   @see testTimestamp()
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestDateTimestamp
 | 
			
		||||
   */
 | 
			
		||||
  public function testDateTimestamp($input, array $initial, array $transform): void {
 | 
			
		||||
    // Initialize a new date object.
 | 
			
		||||
    $date = new DateTimePlus($input, $initial['timezone']);
 | 
			
		||||
    $this->assertDateTimestamp($date, $input, $initial, $transform);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Asserts a DateTimePlus value.
 | 
			
		||||
   *
 | 
			
		||||
   * @param \Drupal\Component\Datetime\DateTimePlus $date
 | 
			
		||||
   *   DateTimePlus to test.
 | 
			
		||||
   * @param string|int $input
 | 
			
		||||
   *   The original input passed to the test method.
 | 
			
		||||
   * @param array $initial
 | 
			
		||||
   *   @see testTimestamp()
 | 
			
		||||
   * @param array $transform
 | 
			
		||||
   *   @see testTimestamp()
 | 
			
		||||
   *
 | 
			
		||||
   * @internal
 | 
			
		||||
   */
 | 
			
		||||
  public function assertDateTimestamp(DateTimePlus $date, string|int $input, array $initial, array $transform): void {
 | 
			
		||||
    // Check format.
 | 
			
		||||
    $value = $date->format($initial['format']);
 | 
			
		||||
    $this->assertEquals($initial['expected_date'], $value, sprintf("Test new DateTimePlus(%s, %s): should be %s, found %s.", $input, $initial['timezone'], $initial['expected_date'], $value));
 | 
			
		||||
 | 
			
		||||
    // Check timezone name.
 | 
			
		||||
    $value = $date->getTimeZone()->getName();
 | 
			
		||||
    $this->assertEquals($initial['expected_timezone'], $value, sprintf("The current timezone is %s: should be %s.", $value, $initial['expected_timezone']));
 | 
			
		||||
 | 
			
		||||
    // Check offset.
 | 
			
		||||
    $value = $date->getOffset();
 | 
			
		||||
    $this->assertEquals($initial['expected_offset'], $value, sprintf("The current offset is %s: should be %s.", $value, $initial['expected_offset']));
 | 
			
		||||
 | 
			
		||||
    // Transform the date to another timezone.
 | 
			
		||||
    $date->setTimezone(new \DateTimeZone($transform['timezone']));
 | 
			
		||||
 | 
			
		||||
    // Check transformed format.
 | 
			
		||||
    $value = $date->format($transform['format']);
 | 
			
		||||
    $this->assertEquals($transform['expected_date'], $value, sprintf("Test \$date->setTimezone(new \\DateTimeZone(%s)): should be %s, found %s.", $transform['timezone'], $transform['expected_date'], $value));
 | 
			
		||||
 | 
			
		||||
    // Check transformed timezone.
 | 
			
		||||
    $value = $date->getTimeZone()->getName();
 | 
			
		||||
    $this->assertEquals($transform['expected_timezone'], $value, sprintf("The current timezone should be %s, found %s.", $transform['expected_timezone'], $value));
 | 
			
		||||
 | 
			
		||||
    // Check transformed offset.
 | 
			
		||||
    $value = $date->getOffset();
 | 
			
		||||
    $this->assertEquals($transform['expected_offset'], $value, sprintf("The current offset should be %s, found %s.", $transform['expected_offset'], $value));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests creating dates from format strings.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $input
 | 
			
		||||
   *   Input argument for DateTimePlus.
 | 
			
		||||
   * @param string $timezone
 | 
			
		||||
   *   Timezone argument for DateTimePlus.
 | 
			
		||||
   * @param string $format
 | 
			
		||||
   *   PHP date() type format for parsing the input.
 | 
			
		||||
   * @param string $format_date
 | 
			
		||||
   *   Format argument for DateTimePlus::format().
 | 
			
		||||
   * @param string $expected
 | 
			
		||||
   *   Expected output from DateTimePlus::format().
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestDateFormat
 | 
			
		||||
   */
 | 
			
		||||
  public function testDateFormat($input, $timezone, $format, $format_date, $expected): void {
 | 
			
		||||
    $date = DateTimePlus::createFromFormat($format, $input, $timezone);
 | 
			
		||||
    $value = $date->format($format_date);
 | 
			
		||||
    $this->assertEquals($expected, $value, sprintf("Test new DateTimePlus(%s, %s, %s): should be %s, found %s.", $input, $timezone, $format, $expected, $value));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests invalid date handling.
 | 
			
		||||
   *
 | 
			
		||||
   * @param mixed $input
 | 
			
		||||
   *   Input argument for DateTimePlus.
 | 
			
		||||
   * @param string $timezone
 | 
			
		||||
   *   Timezone argument for DateTimePlus.
 | 
			
		||||
   * @param string $format
 | 
			
		||||
   *   Format argument for DateTimePlus.
 | 
			
		||||
   * @param string $message
 | 
			
		||||
   *   Message to print if no errors are thrown by the invalid dates.
 | 
			
		||||
   * @param string $class
 | 
			
		||||
   *   The Exception subclass to expect to be thrown.
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestInvalidDates
 | 
			
		||||
   */
 | 
			
		||||
  public function testInvalidDates($input, $timezone, $format, $message, $class): void {
 | 
			
		||||
    $this->expectException($class);
 | 
			
		||||
    DateTimePlus::createFromFormat($format, $input, $timezone);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that DrupalDateTime can detect the right timezone to use.
 | 
			
		||||
   *
 | 
			
		||||
   * When specified or not.
 | 
			
		||||
   *
 | 
			
		||||
   * @param mixed $input
 | 
			
		||||
   *   Input argument for DateTimePlus.
 | 
			
		||||
   * @param mixed $timezone
 | 
			
		||||
   *   Timezone argument for DateTimePlus.
 | 
			
		||||
   * @param string $expected_timezone
 | 
			
		||||
   *   Expected timezone returned from DateTimePlus::getTimezone::getName().
 | 
			
		||||
   * @param string $message
 | 
			
		||||
   *   Message to print on test failure.
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestDateTimezone
 | 
			
		||||
   */
 | 
			
		||||
  public function testDateTimezone($input, $timezone, $expected_timezone, $message): void {
 | 
			
		||||
    $date = new DateTimePlus($input, $timezone);
 | 
			
		||||
    $timezone = $date->getTimezone()->getName();
 | 
			
		||||
    $this->assertEquals($timezone, $expected_timezone, $message);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that DrupalDateTime can detect the correct timezone to use.
 | 
			
		||||
   *
 | 
			
		||||
   * But only when the DrupalDateTime is constructed from a datetime object.
 | 
			
		||||
   */
 | 
			
		||||
  public function testDateTimezoneWithDateTimeObject(): void {
 | 
			
		||||
    // Create a date object with another date object.
 | 
			
		||||
    $input = new \DateTime('now', new \DateTimeZone('Pacific/Midway'));
 | 
			
		||||
    $timezone = NULL;
 | 
			
		||||
    $expected_timezone = 'Pacific/Midway';
 | 
			
		||||
    $message = 'DateTimePlus uses the specified timezone if provided.';
 | 
			
		||||
 | 
			
		||||
    $date = DateTimePlus::createFromDateTime($input, $timezone);
 | 
			
		||||
    $timezone = $date->getTimezone()->getName();
 | 
			
		||||
    $this->assertEquals($timezone, $expected_timezone, $message);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Provides data for date tests.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of arrays, each containing the input parameters for
 | 
			
		||||
   *   DateTimePlusTest::testDates().
 | 
			
		||||
   *
 | 
			
		||||
   * @see DateTimePlusTest::testDates()
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestDates() {
 | 
			
		||||
    $dates = [
 | 
			
		||||
      // String input.
 | 
			
		||||
      // Create date object from datetime string.
 | 
			
		||||
      ['2009-03-07 10:30', 'America/Chicago', '2009-03-07T10:30:00-06:00'],
 | 
			
		||||
      // Same during daylight savings time.
 | 
			
		||||
      ['2009-06-07 10:30', 'America/Chicago', '2009-06-07T10:30:00-05:00'],
 | 
			
		||||
      // Create date object from date string.
 | 
			
		||||
      ['2009-03-07', 'America/Chicago', '2009-03-07T00:00:00-06:00'],
 | 
			
		||||
      // Same during daylight savings time.
 | 
			
		||||
      ['2009-06-07', 'America/Chicago', '2009-06-07T00:00:00-05:00'],
 | 
			
		||||
      // Create date object from date string.
 | 
			
		||||
      ['2009-03-07 10:30', 'Australia/Canberra', '2009-03-07T10:30:00+11:00'],
 | 
			
		||||
      // Same during daylight savings time.
 | 
			
		||||
      ['2009-06-07 10:30', 'Australia/Canberra', '2009-06-07T10:30:00+10:00'],
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    // On 32-bit systems, timestamps are limited to 1901-2038.
 | 
			
		||||
    if (PHP_INT_SIZE > 4) {
 | 
			
		||||
      // Create a date object in the distant past.
 | 
			
		||||
      // @see https://www.drupal.org/node/2795489#comment-12127088
 | 
			
		||||
      // Note that this date is after the United States standardized its
 | 
			
		||||
      // timezones.
 | 
			
		||||
      $dates[] = ['1883-11-19 10:30', 'America/Chicago', '1883-11-19T10:30:00-06:00'];
 | 
			
		||||
      // Create a date object in the far future.
 | 
			
		||||
      $dates[] = ['2345-01-02 02:04', 'UTC', '2345-01-02T02:04:00+00:00'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return $dates;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Provides data for date tests.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of arrays, each containing the input parameters for
 | 
			
		||||
   *   DateTimePlusTest::testDates().
 | 
			
		||||
   *
 | 
			
		||||
   * @see DateTimePlusTest::testDates()
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestDateArrays() {
 | 
			
		||||
    $dates = [
 | 
			
		||||
      // Array input.
 | 
			
		||||
      // Create date object from date array, date only.
 | 
			
		||||
      [['year' => 2010, 'month' => 2, 'day' => 28], 'America/Chicago', '2010-02-28T00:00:00-06:00'],
 | 
			
		||||
      // Create date object from date array with hour.
 | 
			
		||||
      [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 10], 'America/Chicago', '2010-02-28T10:00:00-06:00'],
 | 
			
		||||
      // Create date object from date array, date only.
 | 
			
		||||
      [['year' => 2010, 'month' => 2, 'day' => 28], 'Europe/Berlin', '2010-02-28T00:00:00+01:00'],
 | 
			
		||||
      // Create date object from date array with hour.
 | 
			
		||||
      [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 10], 'Europe/Berlin', '2010-02-28T10:00:00+01:00'],
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    // On 32-bit systems, timestamps are limited to 1901-2038.
 | 
			
		||||
    if (PHP_INT_SIZE > 4) {
 | 
			
		||||
      // Create a date object in the distant past.
 | 
			
		||||
      // @see https://www.drupal.org/node/2795489#comment-12127088
 | 
			
		||||
      // Note that this date is after the United States standardized its
 | 
			
		||||
      // timezones.
 | 
			
		||||
      $dates[] = [['year' => 1883, 'month' => 11, 'day' => 19], 'America/Chicago', '1883-11-19T00:00:00-06:00'];
 | 
			
		||||
      // Create a date object in the far future.
 | 
			
		||||
      $dates[] = [['year' => 2345, 'month' => 1, 'day' => 2], 'UTC', '2345-01-02T00:00:00+00:00'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return $dates;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Provides data for testDateFormats.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of arrays, each containing:
 | 
			
		||||
   *   - 'input' - Input to DateTimePlus.
 | 
			
		||||
   *   - 'timezone' - Timezone for DateTimePlus.
 | 
			
		||||
   *   - 'format' - Date format for DateTimePlus.
 | 
			
		||||
   *   - 'format_date' - Date format for use in $date->format() method.
 | 
			
		||||
   *   - 'expected' - The expected return from DateTimePlus.
 | 
			
		||||
   *
 | 
			
		||||
   * @see testDateFormats()
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestDateFormat() {
 | 
			
		||||
    return [
 | 
			
		||||
      // Create a year-only date.
 | 
			
		||||
      ['2009', NULL, 'Y', 'Y', '2009'],
 | 
			
		||||
      // Create a month and year-only date.
 | 
			
		||||
      ['2009-10', NULL, 'Y-m', 'Y-m', '2009-10'],
 | 
			
		||||
      // Create a time-only date.
 | 
			
		||||
      ['T10:30:00', NULL, '\TH:i:s', 'H:i:s', '10:30:00'],
 | 
			
		||||
      // Create a time-only date.
 | 
			
		||||
      ['10:30:00', NULL, 'H:i:s', 'H:i:s', '10:30:00'],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Provides data for testInvalidDates.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of arrays, each containing:
 | 
			
		||||
   *   - 'input' - Input for DateTimePlus.
 | 
			
		||||
   *   - 'timezone' - Timezone for DateTimePlus.
 | 
			
		||||
   *   - 'format' - Format for DateTimePlus.
 | 
			
		||||
   *   - 'message' - Message to display on failure.
 | 
			
		||||
   *
 | 
			
		||||
   * @see testInvalidDates
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestInvalidDates() {
 | 
			
		||||
    return [
 | 
			
		||||
      // Test for invalid month names when we are using a short version
 | 
			
		||||
      // of the month.
 | 
			
		||||
      ['23 abc 2012', NULL, 'd M Y', "23 abc 2012 contains an invalid month name and did not produce errors.", \InvalidArgumentException::class],
 | 
			
		||||
      // Test for invalid hour.
 | 
			
		||||
      ['0000-00-00T45:30:00', NULL, 'Y-m-d\TH:i:s', "0000-00-00T45:30:00 contains an invalid hour and did not produce errors.", \UnexpectedValueException::class],
 | 
			
		||||
      // Test for invalid day.
 | 
			
		||||
      ['0000-00-99T05:30:00', NULL, 'Y-m-d\TH:i:s', "0000-00-99T05:30:00 contains an invalid day and did not produce errors.", \UnexpectedValueException::class],
 | 
			
		||||
      // Test for invalid month.
 | 
			
		||||
      ['0000-75-00T15:30:00', NULL, 'Y-m-d\TH:i:s', "0000-75-00T15:30:00 contains an invalid month and did not produce errors.", \UnexpectedValueException::class],
 | 
			
		||||
      // Test for invalid year.
 | 
			
		||||
      ['11-08-01T15:30:00', NULL, 'Y-m-d\TH:i:s', "11-08-01T15:30:00 contains an invalid year and did not produce errors.", \UnexpectedValueException::class],
 | 
			
		||||
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider for testInvalidDateArrays.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of arrays, each containing:
 | 
			
		||||
   *   - 'input' - Input for DateTimePlus.
 | 
			
		||||
   *   - 'timezone' - Timezone for DateTimePlus.
 | 
			
		||||
   *
 | 
			
		||||
   * @see testInvalidDateArrays
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestInvalidDateArrays() {
 | 
			
		||||
    return [
 | 
			
		||||
      // One year larger than the documented upper limit of checkdate().
 | 
			
		||||
      [['year' => 32768, 'month' => 1, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
 | 
			
		||||
      // One year smaller than the documented lower limit of checkdate().
 | 
			
		||||
      [['year' => 0, 'month' => 1, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
 | 
			
		||||
      // Test for invalid month from date array.
 | 
			
		||||
      [['year' => 2010, 'month' => 27, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
 | 
			
		||||
      // Test for invalid hour from date array.
 | 
			
		||||
      [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 80, 'minute' => 0, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
 | 
			
		||||
      // Test for invalid minute from date array.
 | 
			
		||||
      [['year' => 2010, 'month' => 7, 'day' => 8, 'hour' => 8, 'minute' => 88, 'second' => 0], 'America/Chicago', \InvalidArgumentException::class],
 | 
			
		||||
      // Regression test for https://www.drupal.org/node/2084455.
 | 
			
		||||
      [['hour' => 59, 'minute' => 1, 'second' => 1], 'America/Chicago', \InvalidArgumentException::class],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider for testCheckArray.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of arrays, each containing:
 | 
			
		||||
   *   - 'array' - Input for DateTimePlus::checkArray().
 | 
			
		||||
   *   - 'expected' - Expected output for  DateTimePlus::checkArray().
 | 
			
		||||
   *
 | 
			
		||||
   * @see testCheckArray
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestCheckArray() {
 | 
			
		||||
    return [
 | 
			
		||||
      'Date array, date only' => [['year' => 2010, 'month' => 2, 'day' => 28], TRUE],
 | 
			
		||||
      'Date array with hour' => [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 10], TRUE],
 | 
			
		||||
      'One year larger than the documented upper limit of checkdate()' => [['year' => 32768, 'month' => 1, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], FALSE],
 | 
			
		||||
      'One year smaller than the documented lower limit of checkdate()' => [['year' => 0, 'month' => 1, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], FALSE],
 | 
			
		||||
      'Invalid month from date array' => [['year' => 2010, 'month' => 27, 'day' => 8, 'hour' => 8, 'minute' => 0, 'second' => 0], FALSE],
 | 
			
		||||
      'Invalid hour from date array' => [['year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 80, 'minute' => 0, 'second' => 0], FALSE],
 | 
			
		||||
      'Invalid minute from date array.' => [['year' => 2010, 'month' => 7, 'day' => 8, 'hour' => 8, 'minute' => 88, 'second' => 0], FALSE],
 | 
			
		||||
      'Missing day' => [['year' => 2059, 'month' => 1, 'second' => 1], FALSE],
 | 
			
		||||
      'Zero day' => [['year' => 2059, 'month' => 1, 'day' => 0], FALSE],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Provides data for testDateTimezone.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of arrays, each containing:
 | 
			
		||||
   *   - 'date' - Date string or object for DateTimePlus.
 | 
			
		||||
   *   - 'timezone' - Timezone string for DateTimePlus.
 | 
			
		||||
   *   - 'expected' - Expected return from
 | 
			
		||||
   *      DateTimePlus::getTimezone()::getName().
 | 
			
		||||
   *   - 'message' - Message to display on test failure.
 | 
			
		||||
   *
 | 
			
		||||
   * @see testDateTimezone
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestDateTimezone() {
 | 
			
		||||
    // Use a common date for most of the tests.
 | 
			
		||||
    $date_string = '2007-01-31 21:00:00';
 | 
			
		||||
 | 
			
		||||
    // Detect the system timezone.
 | 
			
		||||
    $system_timezone = date_default_timezone_get();
 | 
			
		||||
 | 
			
		||||
    return [
 | 
			
		||||
      // Create a date object with an unspecified timezone, which should
 | 
			
		||||
      // end up using the system timezone.
 | 
			
		||||
      [$date_string, NULL, $system_timezone, 'DateTimePlus uses the system timezone when there is no site timezone.'],
 | 
			
		||||
      // Create a date object with a specified timezone name.
 | 
			
		||||
      [$date_string, 'America/Yellowknife', 'America/Yellowknife', 'DateTimePlus uses the specified timezone if provided.'],
 | 
			
		||||
      // Create a date object with a timezone object.
 | 
			
		||||
      [$date_string, new \DateTimeZone('Australia/Canberra'), 'Australia/Canberra', 'DateTimePlus uses the specified timezone if provided.'],
 | 
			
		||||
      // Create a date object with another date object.
 | 
			
		||||
      [new DateTimePlus('now', 'Pacific/Midway'), NULL, 'Pacific/Midway', 'DateTimePlus uses the specified timezone if provided.'],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Provides data for testTimestamp.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of arrays, each containing the arguments required for
 | 
			
		||||
   *   self::testTimestamp().
 | 
			
		||||
   *
 | 
			
		||||
   * @see testTimestamp()
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestTimestamp() {
 | 
			
		||||
    return [
 | 
			
		||||
      // Create date object from a unix timestamp and display it in
 | 
			
		||||
      // local time.
 | 
			
		||||
      [
 | 
			
		||||
        'input' => 0,
 | 
			
		||||
        'initial' => [
 | 
			
		||||
          'timezone' => 'UTC',
 | 
			
		||||
          'format' => 'c',
 | 
			
		||||
          'expected_date' => '1970-01-01T00:00:00+00:00',
 | 
			
		||||
          'expected_timezone' => 'UTC',
 | 
			
		||||
          'expected_offset' => 0,
 | 
			
		||||
        ],
 | 
			
		||||
        'transform' => [
 | 
			
		||||
          'timezone' => 'America/Los_Angeles',
 | 
			
		||||
          'format' => 'c',
 | 
			
		||||
          'expected_date' => '1969-12-31T16:00:00-08:00',
 | 
			
		||||
          'expected_timezone' => 'America/Los_Angeles',
 | 
			
		||||
          'expected_offset' => '-28800',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      // Create a date using the timestamp of zero, then display its
 | 
			
		||||
      // value both in UTC and the local timezone.
 | 
			
		||||
      [
 | 
			
		||||
        'input' => 0,
 | 
			
		||||
        'initial' => [
 | 
			
		||||
          'timezone' => 'America/Los_Angeles',
 | 
			
		||||
          'format' => 'c',
 | 
			
		||||
          'expected_date' => '1969-12-31T16:00:00-08:00',
 | 
			
		||||
          'expected_timezone' => 'America/Los_Angeles',
 | 
			
		||||
          'expected_offset' => '-28800',
 | 
			
		||||
        ],
 | 
			
		||||
        'transform' => [
 | 
			
		||||
          'timezone' => 'UTC',
 | 
			
		||||
          'format' => 'c',
 | 
			
		||||
          'expected_date' => '1970-01-01T00:00:00+00:00',
 | 
			
		||||
          'expected_timezone' => 'UTC',
 | 
			
		||||
          'expected_offset' => 0,
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Provides data for testDateTimestamp.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of arrays, each containing the arguments required for
 | 
			
		||||
   *   self::testDateTimestamp().
 | 
			
		||||
   *
 | 
			
		||||
   * @see testDateTimestamp()
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestDateTimestamp() {
 | 
			
		||||
    return [
 | 
			
		||||
      // Create date object from datetime string in UTC, and convert
 | 
			
		||||
      // it to a local date.
 | 
			
		||||
      [
 | 
			
		||||
        'input' => '1970-01-01 00:00:00',
 | 
			
		||||
        'initial' => [
 | 
			
		||||
          'timezone' => 'UTC',
 | 
			
		||||
          'format' => 'c',
 | 
			
		||||
          'expected_date' => '1970-01-01T00:00:00+00:00',
 | 
			
		||||
          'expected_timezone' => 'UTC',
 | 
			
		||||
          'expected_offset' => 0,
 | 
			
		||||
        ],
 | 
			
		||||
        'transform' => [
 | 
			
		||||
          'timezone' => 'America/Los_Angeles',
 | 
			
		||||
          'format' => 'c',
 | 
			
		||||
          'expected_date' => '1969-12-31T16:00:00-08:00',
 | 
			
		||||
          'expected_timezone' => 'America/Los_Angeles',
 | 
			
		||||
          'expected_offset' => '-28800',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      // Convert the local time to UTC using string input.
 | 
			
		||||
      [
 | 
			
		||||
        'input' => '1969-12-31 16:00:00',
 | 
			
		||||
        'initial' => [
 | 
			
		||||
          'timezone' => 'America/Los_Angeles',
 | 
			
		||||
          'format' => 'c',
 | 
			
		||||
          'expected_date' => '1969-12-31T16:00:00-08:00',
 | 
			
		||||
          'expected_timezone' => 'America/Los_Angeles',
 | 
			
		||||
          'expected_offset' => '-28800',
 | 
			
		||||
        ],
 | 
			
		||||
        'transform' => [
 | 
			
		||||
          'timezone' => 'UTC',
 | 
			
		||||
          'format' => 'c',
 | 
			
		||||
          'expected_date' => '1970-01-01T00:00:00+00:00',
 | 
			
		||||
          'expected_timezone' => 'UTC',
 | 
			
		||||
          'expected_offset' => 0,
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      // Convert the local time to UTC using string input.
 | 
			
		||||
      [
 | 
			
		||||
        'input' => '1969-12-31 16:00:00',
 | 
			
		||||
        'initial' => [
 | 
			
		||||
          'timezone' => 'Europe/Warsaw',
 | 
			
		||||
          'format' => 'c',
 | 
			
		||||
          'expected_date' => '1969-12-31T16:00:00+01:00',
 | 
			
		||||
          'expected_timezone' => 'Europe/Warsaw',
 | 
			
		||||
          'expected_offset' => '+3600',
 | 
			
		||||
        ],
 | 
			
		||||
        'transform' => [
 | 
			
		||||
          'timezone' => 'UTC',
 | 
			
		||||
          'format' => 'c',
 | 
			
		||||
          'expected_date' => '1969-12-31T15:00:00+00:00',
 | 
			
		||||
          'expected_timezone' => 'UTC',
 | 
			
		||||
          'expected_offset' => 0,
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Provides data for date tests.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of arrays, each containing the input parameters for
 | 
			
		||||
   *   DateTimePlusTest::testDateDiff().
 | 
			
		||||
   *
 | 
			
		||||
   * @see DateTimePlusTest::testDateDiff()
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestDateDiff() {
 | 
			
		||||
 | 
			
		||||
    $empty_interval = new \DateInterval('PT0S');
 | 
			
		||||
 | 
			
		||||
    $positive_19_hours = new \DateInterval('PT19H');
 | 
			
		||||
 | 
			
		||||
    $positive_18_hours = new \DateInterval('PT18H');
 | 
			
		||||
 | 
			
		||||
    $positive_1_hour = new \DateInterval('PT1H');
 | 
			
		||||
 | 
			
		||||
    $negative_1_hour = new \DateInterval('PT1H');
 | 
			
		||||
    $negative_1_hour->invert = 1;
 | 
			
		||||
 | 
			
		||||
    return [
 | 
			
		||||
      // There should be a 19 hour time interval between
 | 
			
		||||
      // new years in Sydney and new years in LA in year 2000.
 | 
			
		||||
      [
 | 
			
		||||
        'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')),
 | 
			
		||||
        'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')),
 | 
			
		||||
        'absolute' => FALSE,
 | 
			
		||||
        'expected' => $positive_19_hours,
 | 
			
		||||
      ],
 | 
			
		||||
      // In 1970 Sydney did not observe daylight savings time
 | 
			
		||||
      // So there is only an 18 hour time interval.
 | 
			
		||||
      [
 | 
			
		||||
        'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')),
 | 
			
		||||
        'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')),
 | 
			
		||||
        'absolute' => FALSE,
 | 
			
		||||
        'expected' => $positive_18_hours,
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'input1' => DateTimePlus::createFromFormat('U', '3600', new \DateTimeZone('America/Los_Angeles')),
 | 
			
		||||
        'input2' => DateTimePlus::createFromTimestamp(0, new \DateTimeZone('UTC')),
 | 
			
		||||
        'absolute' => FALSE,
 | 
			
		||||
        'expected' => $negative_1_hour,
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'input1' => DateTimePlus::createFromTimestamp(3600),
 | 
			
		||||
        'input2' => DateTimePlus::createFromTimestamp(0),
 | 
			
		||||
        'absolute' => FALSE,
 | 
			
		||||
        'expected' => $negative_1_hour,
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'input1' => DateTimePlus::createFromTimestamp(3600),
 | 
			
		||||
        'input2' => \DateTime::createFromFormat('U', '0'),
 | 
			
		||||
        'absolute' => FALSE,
 | 
			
		||||
        'expected' => $negative_1_hour,
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'input1' => DateTimePlus::createFromTimestamp(3600),
 | 
			
		||||
        'input2' => DateTimePlus::createFromTimestamp(0),
 | 
			
		||||
        'absolute' => TRUE,
 | 
			
		||||
        'expected' => $positive_1_hour,
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'input1' => DateTimePlus::createFromTimestamp(3600),
 | 
			
		||||
        'input2' => \DateTime::createFromFormat('U', '0'),
 | 
			
		||||
        'absolute' => TRUE,
 | 
			
		||||
        'expected' => $positive_1_hour,
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'input1' => DateTimePlus::createFromTimestamp(0),
 | 
			
		||||
        'input2' => DateTimePlus::createFromTimestamp(0),
 | 
			
		||||
        'absolute' => FALSE,
 | 
			
		||||
        'expected' => $empty_interval,
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Provides data for date tests.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of arrays, each containing the input parameters for
 | 
			
		||||
   *   DateTimePlusTest::testInvalidDateDiff().
 | 
			
		||||
   *
 | 
			
		||||
   * @see DateTimePlusTest::testInvalidDateDiff()
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestInvalidDateDiff() {
 | 
			
		||||
    return [
 | 
			
		||||
      [
 | 
			
		||||
        'input1' => DateTimePlus::createFromTimestamp(3600),
 | 
			
		||||
        'input2' => '1970-01-01 00:00:00',
 | 
			
		||||
        'absolute' => FALSE,
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'input1' => DateTimePlus::createFromTimestamp(3600),
 | 
			
		||||
        'input2' => NULL,
 | 
			
		||||
        'absolute' => FALSE,
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests invalid values passed to constructor.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $time
 | 
			
		||||
   *   A date/time string.
 | 
			
		||||
   * @param string[] $errors
 | 
			
		||||
   *   An array of error messages.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::__construct
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestInvalidConstructor
 | 
			
		||||
   */
 | 
			
		||||
  public function testInvalidConstructor($time, array $errors): void {
 | 
			
		||||
    $date = new DateTimePlus($time);
 | 
			
		||||
 | 
			
		||||
    $this->assertEquals(TRUE, $date->hasErrors());
 | 
			
		||||
    $this->assertEquals($errors, $date->getErrors());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Provider for testInvalidConstructor().
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of invalid date/time strings, and corresponding error messages.
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestInvalidConstructor() {
 | 
			
		||||
    return [
 | 
			
		||||
      [
 | 
			
		||||
        'YYYY-MM-DD',
 | 
			
		||||
        [
 | 
			
		||||
          'The timezone could not be found in the database',
 | 
			
		||||
          'Unexpected character',
 | 
			
		||||
          'Double timezone specification',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        '2017-MM-DD',
 | 
			
		||||
        [
 | 
			
		||||
          'Unexpected character',
 | 
			
		||||
          'The timezone could not be found in the database',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'YYYY-03-DD',
 | 
			
		||||
        [
 | 
			
		||||
          'The timezone could not be found in the database',
 | 
			
		||||
          'Unexpected character',
 | 
			
		||||
          'Double timezone specification',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'YYYY-MM-07',
 | 
			
		||||
        [
 | 
			
		||||
          'The timezone could not be found in the database',
 | 
			
		||||
          'Unexpected character',
 | 
			
		||||
          'Double timezone specification',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        '2017-13-55',
 | 
			
		||||
        [
 | 
			
		||||
          'Unexpected character',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'YYYY-MM-DD hh:mm:ss',
 | 
			
		||||
        [
 | 
			
		||||
          'The timezone could not be found in the database',
 | 
			
		||||
          'Unexpected character',
 | 
			
		||||
          'Double timezone specification',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        '2017-03-07 25:70:80',
 | 
			
		||||
        [
 | 
			
		||||
          'Unexpected character',
 | 
			
		||||
          'Double time specification',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'invalid time string',
 | 
			
		||||
        [
 | 
			
		||||
          'The timezone could not be found in the database',
 | 
			
		||||
          'Double timezone specification',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests the $settings['validate_format'] parameter in ::createFromFormat().
 | 
			
		||||
   */
 | 
			
		||||
  public function testValidateFormat(): void {
 | 
			
		||||
    // Check that an input that does not strictly follow the input format will
 | 
			
		||||
    // produce the desired date. In this case the year string '11' doesn't
 | 
			
		||||
    // precisely match the 'Y' formatter parameter, but PHP will parse it
 | 
			
		||||
    // regardless. However, when formatted with the same string, the year will
 | 
			
		||||
    // be output with four digits. With the ['validate_format' => FALSE]
 | 
			
		||||
    // $settings, this will not thrown an exception.
 | 
			
		||||
    $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '11-03-31 17:44:00', 'UTC', ['validate_format' => FALSE]);
 | 
			
		||||
    $this->assertEquals('0011-03-31 17:44:00', $date->format('Y-m-d H:i:s'));
 | 
			
		||||
 | 
			
		||||
    // Parse the same date with ['validate_format' => TRUE] and make sure we
 | 
			
		||||
    // get the expected exception.
 | 
			
		||||
    $this->expectException(\UnexpectedValueException::class);
 | 
			
		||||
    $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '11-03-31 17:44:00', 'UTC', ['validate_format' => TRUE]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests setting the default time for date-only objects.
 | 
			
		||||
   */
 | 
			
		||||
  public function testDefaultDateTime(): void {
 | 
			
		||||
    $utc = new \DateTimeZone('UTC');
 | 
			
		||||
 | 
			
		||||
    $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '2017-05-23 22:58:00', $utc);
 | 
			
		||||
    $this->assertEquals('22:58:00', $date->format('H:i:s'));
 | 
			
		||||
    $date->setDefaultDateTime();
 | 
			
		||||
    $this->assertEquals('12:00:00', $date->format('H:i:s'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that object methods are chainable.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::__call
 | 
			
		||||
   */
 | 
			
		||||
  public function testChainable(): void {
 | 
			
		||||
    $date = new DateTimePlus('now', 'Australia/Sydney');
 | 
			
		||||
 | 
			
		||||
    $date->setTimestamp(12345678);
 | 
			
		||||
    $rendered = $date->render();
 | 
			
		||||
    $this->assertEquals('1970-05-24 07:21:18 Australia/Sydney', $rendered);
 | 
			
		||||
 | 
			
		||||
    $date->setTimestamp(23456789);
 | 
			
		||||
    $rendered = $date->setTimezone(new \DateTimeZone('America/New_York'))->render();
 | 
			
		||||
    $this->assertEquals('1970-09-29 07:46:29 America/New_York', $rendered);
 | 
			
		||||
 | 
			
		||||
    $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-05-24 07:21:18', new \DateTimeZone('Australia/Sydney'))
 | 
			
		||||
      ->setTimezone(new \DateTimeZone('America/New_York'));
 | 
			
		||||
    $rendered = $date->render();
 | 
			
		||||
    $this->assertInstanceOf(DateTimePlus::class, $date);
 | 
			
		||||
    $this->assertEquals(12345678, $date->getTimestamp());
 | 
			
		||||
    $this->assertEquals('1970-05-23 17:21:18 America/New_York', $rendered);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that non-chainable methods work.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::__call
 | 
			
		||||
   */
 | 
			
		||||
  public function testChainableNonChainable(): void {
 | 
			
		||||
    $datetime1 = new DateTimePlus('2009-10-11 12:00:00');
 | 
			
		||||
    $datetime2 = new DateTimePlus('2009-10-13 12:00:00');
 | 
			
		||||
    $interval = $datetime1->diff($datetime2);
 | 
			
		||||
    $this->assertInstanceOf(\DateInterval::class, $interval);
 | 
			
		||||
    $this->assertEquals('+2 days', $interval->format('%R%a days'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that chained calls to non-existent functions throw an exception.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::__call
 | 
			
		||||
   */
 | 
			
		||||
  public function testChainableNonCallable(): void {
 | 
			
		||||
    $this->expectException(\BadMethodCallException::class);
 | 
			
		||||
    $this->expectExceptionMessage('Call to undefined method Drupal\Component\Datetime\DateTimePlus::nonexistent()');
 | 
			
		||||
    $date = new DateTimePlus('now', 'Australia/Sydney');
 | 
			
		||||
    $date->setTimezone(new \DateTimeZone('America/New_York'))->nonexistent();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getPhpDateTime
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetPhpDateTime(): void {
 | 
			
		||||
    $new_york = new \DateTimeZone('America/New_York');
 | 
			
		||||
    $berlin = new \DateTimeZone('Europe/Berlin');
 | 
			
		||||
 | 
			
		||||
    // Test retrieving a cloned copy of the wrapped \DateTime object, and that
 | 
			
		||||
    // altering it does not change the DateTimePlus object.
 | 
			
		||||
    $date_time_plus = DateTimePlus::createFromFormat('Y-m-d H:i:s', '2017-07-13 22:40:00', $new_york, ['langcode' => 'en']);
 | 
			
		||||
    $this->assertEquals(1500000000, $date_time_plus->getTimestamp());
 | 
			
		||||
    $this->assertEquals('America/New_York', $date_time_plus->getTimezone()->getName());
 | 
			
		||||
 | 
			
		||||
    $datetime = $date_time_plus->getPhpDateTime();
 | 
			
		||||
    $this->assertInstanceOf('DateTime', $datetime);
 | 
			
		||||
    $this->assertEquals(1500000000, $datetime->getTimestamp());
 | 
			
		||||
    $this->assertEquals('America/New_York', $datetime->getTimezone()->getName());
 | 
			
		||||
 | 
			
		||||
    $datetime->setTimestamp(1400000000)->setTimezone($berlin);
 | 
			
		||||
    $this->assertEquals(1400000000, $datetime->getTimestamp());
 | 
			
		||||
    $this->assertEquals('Europe/Berlin', $datetime->getTimezone()->getName());
 | 
			
		||||
    $this->assertEquals(1500000000, $date_time_plus->getTimestamp());
 | 
			
		||||
    $this->assertEquals('America/New_York', $date_time_plus->getTimezone()->getName());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										151
									
								
								web/core/tests/Drupal/Tests/Component/Datetime/TimeTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								web/core/tests/Drupal/Tests/Component/Datetime/TimeTest.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,151 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Datetime;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Datetime\Time;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Symfony\Component\HttpFoundation\Request;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests the Time class.
 | 
			
		||||
 *
 | 
			
		||||
 * Isolate the tests to prevent side effects from altering system time.
 | 
			
		||||
 *
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Datetime\Time
 | 
			
		||||
 * @group Datetime
 | 
			
		||||
 * @runTestsInSeparateProcesses
 | 
			
		||||
 * @preserveGlobalState disabled
 | 
			
		||||
 */
 | 
			
		||||
class TimeTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The mocked request stack.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Symfony\Component\HttpFoundation\RequestStack|\PHPUnit\Framework\MockObject\MockObject
 | 
			
		||||
   */
 | 
			
		||||
  protected $requestStack;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The mocked time class.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Drupal\Component\Datetime\Time
 | 
			
		||||
   */
 | 
			
		||||
  protected $time;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    $this->requestStack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->getMock();
 | 
			
		||||
    $this->time = new Time($this->requestStack);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests the getRequestTime method.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getRequestTime
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetRequestTime(): void {
 | 
			
		||||
    $expected = 12345678;
 | 
			
		||||
 | 
			
		||||
    $request = Request::createFromGlobals();
 | 
			
		||||
    $request->server->set('REQUEST_TIME', $expected);
 | 
			
		||||
 | 
			
		||||
    // Mocks a the request stack getting the current request.
 | 
			
		||||
    $this->requestStack->expects($this->any())
 | 
			
		||||
      ->method('getCurrentRequest')
 | 
			
		||||
      ->willReturn($request);
 | 
			
		||||
 | 
			
		||||
    $this->assertEquals($expected, $this->time->getRequestTime());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests the getRequestMicroTime method.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getRequestMicroTime
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetRequestMicroTime(): void {
 | 
			
		||||
    $expected = 1234567.89;
 | 
			
		||||
 | 
			
		||||
    $request = Request::createFromGlobals();
 | 
			
		||||
    $request->server->set('REQUEST_TIME_FLOAT', $expected);
 | 
			
		||||
 | 
			
		||||
    // Mocks a the request stack getting the current request.
 | 
			
		||||
    $this->requestStack->expects($this->any())
 | 
			
		||||
      ->method('getCurrentRequest')
 | 
			
		||||
      ->willReturn($request);
 | 
			
		||||
 | 
			
		||||
    $this->assertEquals($expected, $this->time->getRequestMicroTime());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getRequestTime
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetRequestTimeNoRequest(): void {
 | 
			
		||||
    // With no request, and no global variable, we expect to get the int part
 | 
			
		||||
    // of the microtime.
 | 
			
		||||
    $expected = 1234567;
 | 
			
		||||
    unset($_SERVER['REQUEST_TIME']);
 | 
			
		||||
    $this->assertEquals($expected, $this->time->getRequestTime());
 | 
			
		||||
    $_SERVER['REQUEST_TIME'] = 23456789;
 | 
			
		||||
    $this->assertEquals(23456789, $this->time->getRequestTime());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getRequestMicroTime
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetRequestMicroTimeNoRequest(): void {
 | 
			
		||||
    $expected = 1234567.89;
 | 
			
		||||
    unset($_SERVER['REQUEST_TIME_FLOAT']);
 | 
			
		||||
    $this->assertEquals($expected, $this->time->getRequestMicroTime());
 | 
			
		||||
    $_SERVER['REQUEST_TIME_FLOAT'] = 2345678.90;
 | 
			
		||||
    $this->assertEquals(2345678.90, $this->time->getRequestMicroTime());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests the getCurrentTime method.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getCurrentTime
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetCurrentTime(): void {
 | 
			
		||||
    $expected = 12345678;
 | 
			
		||||
    $this->assertEquals($expected, $this->time->getCurrentTime());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests the getCurrentMicroTime method.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getCurrentMicroTime
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetCurrentMicroTime(): void {
 | 
			
		||||
    $expected = 1234567.89;
 | 
			
		||||
    $this->assertEquals($expected, $this->time->getCurrentMicroTime());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Component\Datetime;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Shadow time() system call.
 | 
			
		||||
 *
 | 
			
		||||
 * @return int
 | 
			
		||||
 *   The fixed integer timestamp used for testing purposes.
 | 
			
		||||
 */
 | 
			
		||||
function time() {
 | 
			
		||||
  return 12345678;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Shadow microtime system call.
 | 
			
		||||
 *
 | 
			
		||||
 * @return float
 | 
			
		||||
 *   The fixed float timestamp used for testing purposes.
 | 
			
		||||
 */
 | 
			
		||||
function microtime(bool $as_float = FALSE) {
 | 
			
		||||
  return 1234567.89;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,62 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Datetime;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Datetime\Time;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests that getRequest(Micro)Time works when no underlying request exists.
 | 
			
		||||
 *
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Datetime\Time
 | 
			
		||||
 * @group Datetime
 | 
			
		||||
 * @group #slow
 | 
			
		||||
 * @runTestsInSeparateProcesses
 | 
			
		||||
 * @preserveGlobalState disabled
 | 
			
		||||
 */
 | 
			
		||||
class TimeWithNoRequestTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The time class for testing.
 | 
			
		||||
   */
 | 
			
		||||
  protected Time $time;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    // We need to explicitly unset the $_SERVER variables, so that Time is
 | 
			
		||||
    // forced to look for current time.
 | 
			
		||||
    unset($_SERVER['REQUEST_TIME']);
 | 
			
		||||
    unset($_SERVER['REQUEST_TIME_FLOAT']);
 | 
			
		||||
 | 
			
		||||
    $this->time = new Time();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests the getRequestTime method.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getRequestTime
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetRequestTimeImmutable(): void {
 | 
			
		||||
    $requestTime = $this->time->getRequestTime();
 | 
			
		||||
    sleep(2);
 | 
			
		||||
    $this->assertSame($requestTime, $this->time->getRequestTime());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests the getRequestMicroTime method.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getRequestMicroTime
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetRequestMicroTimeImmutable(): void {
 | 
			
		||||
    $requestTime = $this->time->getRequestMicroTime();
 | 
			
		||||
    usleep(20000);
 | 
			
		||||
    $this->assertSame($requestTime, $this->time->getRequestMicroTime());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -0,0 +1,755 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\DependencyInjection\Dumper;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Utility\Crypt;
 | 
			
		||||
use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Prophecy\PhpUnit\ProphecyTrait;
 | 
			
		||||
use Prophecy\Prophet;
 | 
			
		||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
 | 
			
		||||
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
 | 
			
		||||
use Symfony\Component\DependencyInjection\ContainerInterface;
 | 
			
		||||
use Symfony\Component\DependencyInjection\Definition;
 | 
			
		||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 | 
			
		||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
 | 
			
		||||
use Symfony\Component\DependencyInjection\Parameter;
 | 
			
		||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
 | 
			
		||||
use Symfony\Component\DependencyInjection\Reference;
 | 
			
		||||
use Symfony\Component\ExpressionLanguage\Expression;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\DependencyInjection\Dumper\OptimizedPhpArrayDumper
 | 
			
		||||
 * @group DependencyInjection
 | 
			
		||||
 */
 | 
			
		||||
class OptimizedPhpArrayDumperTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  use ExpectDeprecationTrait;
 | 
			
		||||
  use ProphecyTrait;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The container builder instance.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Symfony\Component\DependencyInjection\ContainerBuilder
 | 
			
		||||
   */
 | 
			
		||||
  protected $containerBuilder;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The definition for the container to build in tests.
 | 
			
		||||
   *
 | 
			
		||||
   * @var array
 | 
			
		||||
   */
 | 
			
		||||
  protected $containerDefinition;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Whether the dumper uses the machine-optimized format or not.
 | 
			
		||||
   *
 | 
			
		||||
   * @var bool
 | 
			
		||||
   */
 | 
			
		||||
  protected $machineFormat = TRUE;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Stores the dumper class to use.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   */
 | 
			
		||||
  protected $dumperClass = '\Drupal\Component\DependencyInjection\Dumper\OptimizedPhpArrayDumper';
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The dumper instance.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Symfony\Component\DependencyInjection\Dumper\DumperInterface
 | 
			
		||||
   */
 | 
			
		||||
  protected $dumper;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    // Setup a mock container builder.
 | 
			
		||||
    $this->containerBuilder = $this->prophesize('\Symfony\Component\DependencyInjection\ContainerBuilder');
 | 
			
		||||
    $this->containerBuilder->getAliases()->willReturn([]);
 | 
			
		||||
    $this->containerBuilder->getParameterBag()->willReturn(new ParameterBag());
 | 
			
		||||
    $this->containerBuilder->getDefinitions()->willReturn([]);
 | 
			
		||||
    $this->containerBuilder->isCompiled()->willReturn(TRUE);
 | 
			
		||||
 | 
			
		||||
    $definition = [];
 | 
			
		||||
    $definition['aliases'] = [];
 | 
			
		||||
    $definition['parameters'] = [];
 | 
			
		||||
    $definition['services'] = [];
 | 
			
		||||
    $definition['frozen'] = TRUE;
 | 
			
		||||
    $definition['machine_format'] = $this->machineFormat;
 | 
			
		||||
 | 
			
		||||
    $this->containerDefinition = $definition;
 | 
			
		||||
 | 
			
		||||
    // Create the dumper.
 | 
			
		||||
    $this->dumper = new $this->dumperClass($this->containerBuilder->reveal());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that an empty container works properly.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::dump
 | 
			
		||||
   * @covers ::getArray
 | 
			
		||||
   * @covers ::supportsMachineFormat
 | 
			
		||||
   */
 | 
			
		||||
  public function testDumpForEmptyContainer(): void {
 | 
			
		||||
    $serialized_definition = $this->dumper->dump();
 | 
			
		||||
    $this->assertEquals(serialize($this->containerDefinition), $serialized_definition);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that alias processing works properly.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getAliases
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider getAliasesDataProvider
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetAliases($aliases, $definition_aliases): void {
 | 
			
		||||
    $this->containerDefinition['aliases'] = $definition_aliases;
 | 
			
		||||
    $this->containerBuilder->getAliases()->willReturn($aliases);
 | 
			
		||||
    $this->assertEquals($this->containerDefinition, $this->dumper->getArray(), 'Expected definition matches dump.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider for testGetAliases().
 | 
			
		||||
   *
 | 
			
		||||
   * @return array[]
 | 
			
		||||
   *   Returns data-set elements with:
 | 
			
		||||
   *     - aliases as returned by ContainerBuilder.
 | 
			
		||||
   *     - aliases as expected in the container definition.
 | 
			
		||||
   */
 | 
			
		||||
  public static function getAliasesDataProvider() {
 | 
			
		||||
    return [
 | 
			
		||||
      [[], []],
 | 
			
		||||
      [
 | 
			
		||||
        ['foo' => 'foo.alias'],
 | 
			
		||||
        ['foo' => 'foo.alias'],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        ['foo' => 'foo.alias', 'foo.alias' => 'foo.alias.alias'],
 | 
			
		||||
        ['foo' => 'foo.alias.alias', 'foo.alias' => 'foo.alias.alias'],
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that parameter processing works properly.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getParameters
 | 
			
		||||
   * @covers ::prepareParameters
 | 
			
		||||
   * @covers ::escape
 | 
			
		||||
   * @covers ::dumpValue
 | 
			
		||||
   * @covers ::getReferenceCall
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider getParametersDataProvider
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetParameters($parameters, $definition_parameters, $is_frozen): void {
 | 
			
		||||
    $this->containerDefinition['parameters'] = $definition_parameters;
 | 
			
		||||
    $this->containerDefinition['frozen'] = $is_frozen;
 | 
			
		||||
 | 
			
		||||
    $parameter_bag = new ParameterBag($parameters);
 | 
			
		||||
    $this->containerBuilder->getParameterBag()->willReturn($parameter_bag);
 | 
			
		||||
    $this->containerBuilder->isCompiled()->willReturn($is_frozen);
 | 
			
		||||
 | 
			
		||||
    $this->assertEquals($this->containerDefinition, $this->dumper->getArray(), 'Expected definition matches dump.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider for testGetParameters().
 | 
			
		||||
   *
 | 
			
		||||
   * @return array[]
 | 
			
		||||
   *   Returns data-set elements with:
 | 
			
		||||
   *     - parameters as returned by ContainerBuilder.
 | 
			
		||||
   *     - parameters as expected in the container definition.
 | 
			
		||||
   *     - frozen value
 | 
			
		||||
   */
 | 
			
		||||
  public static function getParametersDataProvider() {
 | 
			
		||||
    return [
 | 
			
		||||
      [[], [], TRUE],
 | 
			
		||||
      [
 | 
			
		||||
        ['foo' => 'value_foo'],
 | 
			
		||||
        ['foo' => 'value_foo'],
 | 
			
		||||
        TRUE,
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        ['foo' => ['llama' => 'yes']],
 | 
			
		||||
        ['foo' => ['llama' => 'yes']],
 | 
			
		||||
        TRUE,
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        ['foo' => '%llama%', 'llama' => 'yes'],
 | 
			
		||||
        ['foo' => '%%llama%%', 'llama' => 'yes'],
 | 
			
		||||
        TRUE,
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        ['foo' => '%llama%', 'llama' => 'yes'],
 | 
			
		||||
        ['foo' => '%llama%', 'llama' => 'yes'],
 | 
			
		||||
        FALSE,
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that service processing works properly.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getServiceDefinitions
 | 
			
		||||
   * @covers ::getServiceDefinition
 | 
			
		||||
   * @covers ::dumpMethodCalls
 | 
			
		||||
   * @covers ::dumpCollection
 | 
			
		||||
   * @covers ::dumpCallable
 | 
			
		||||
   * @covers ::dumpValue
 | 
			
		||||
   * @covers ::getPrivateServiceCall
 | 
			
		||||
   * @covers ::getReferenceCall
 | 
			
		||||
   * @covers ::getServiceCall
 | 
			
		||||
   * @covers ::getServiceClosureCall
 | 
			
		||||
   * @covers ::getParameterCall
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider getDefinitionsDataProvider
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetServiceDefinitions($services, $definition_services): void {
 | 
			
		||||
    $this->containerDefinition['services'] = $definition_services;
 | 
			
		||||
 | 
			
		||||
    $this->containerBuilder->getDefinitions()->willReturn($services);
 | 
			
		||||
 | 
			
		||||
    $bar_definition = new Definition('\stdClass');
 | 
			
		||||
    $bar_definition->setPublic(TRUE);
 | 
			
		||||
    $this->containerBuilder->getDefinition('bar')->willReturn($bar_definition);
 | 
			
		||||
 | 
			
		||||
    $private_definition = new Definition('\stdClass');
 | 
			
		||||
    $private_definition->setPublic(FALSE);
 | 
			
		||||
 | 
			
		||||
    $this->containerBuilder->getDefinition('private_definition')->willReturn($private_definition);
 | 
			
		||||
 | 
			
		||||
    $this->assertEquals($this->containerDefinition, $this->dumper->getArray(), 'Expected definition matches dump.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider for testGetServiceDefinitions().
 | 
			
		||||
   *
 | 
			
		||||
   * @return array[]
 | 
			
		||||
   *   Returns data-set elements with:
 | 
			
		||||
   *     - parameters as returned by ContainerBuilder.
 | 
			
		||||
   *     - parameters as expected in the container definition.
 | 
			
		||||
   *     - frozen value
 | 
			
		||||
   */
 | 
			
		||||
  public static function getDefinitionsDataProvider() {
 | 
			
		||||
    $base_service_definition = [
 | 
			
		||||
      'class' => '\stdClass',
 | 
			
		||||
      'public' => TRUE,
 | 
			
		||||
      'file' => FALSE,
 | 
			
		||||
      'synthetic' => FALSE,
 | 
			
		||||
      'lazy' => FALSE,
 | 
			
		||||
      'arguments' => [],
 | 
			
		||||
      'arguments_count' => 0,
 | 
			
		||||
      'properties' => [],
 | 
			
		||||
      'calls' => [],
 | 
			
		||||
      'shared' => TRUE,
 | 
			
		||||
      'factory' => FALSE,
 | 
			
		||||
      'configurator' => FALSE,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    // Test basic flags.
 | 
			
		||||
    $service_definitions[] = [] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'public' => FALSE,
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'file' => 'test_include.php',
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'synthetic' => TRUE,
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'shared' => FALSE,
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'lazy' => TRUE,
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test a basic public Reference.
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'arguments' => ['foo', new Reference('bar')],
 | 
			
		||||
      'arguments_count' => 2,
 | 
			
		||||
      'arguments_expected' => static::getCollection(['foo', static::getServiceCall('bar')]),
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test a public reference that should not throw an Exception.
 | 
			
		||||
    $reference = new Reference('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE);
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'arguments' => [$reference],
 | 
			
		||||
      'arguments_count' => 1,
 | 
			
		||||
      'arguments_expected' => static::getCollection([static::getServiceCall('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE)]),
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test a private shared service, denoted by having a Reference.
 | 
			
		||||
    $private_definition = [
 | 
			
		||||
      'class' => '\stdClass',
 | 
			
		||||
      'public' => FALSE,
 | 
			
		||||
      'arguments_count' => 0,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'arguments' => ['foo', new Reference('private_definition')],
 | 
			
		||||
      'arguments_count' => 2,
 | 
			
		||||
      'arguments_expected' => static::getCollection([
 | 
			
		||||
        'foo',
 | 
			
		||||
        static::getPrivateServiceCall('private_definition', $private_definition, TRUE),
 | 
			
		||||
      ]),
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test a service closure.
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'arguments' => [
 | 
			
		||||
        'foo',
 | 
			
		||||
        [
 | 
			
		||||
          'alias-1' => new ServiceClosureArgument(new Reference('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE)),
 | 
			
		||||
          'alias-2' => new ServiceClosureArgument(new Reference('bar', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)),
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      'arguments_count' => 2,
 | 
			
		||||
      'arguments_expected' => static::getCollection([
 | 
			
		||||
        'foo',
 | 
			
		||||
        static::getCollection([
 | 
			
		||||
          'alias-1' => static::getServiceClosureCall('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE),
 | 
			
		||||
          'alias-2' => static::getServiceClosureCall('bar', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE),
 | 
			
		||||
        ]),
 | 
			
		||||
      ]),
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test a private non-shared service, denoted by having a Definition.
 | 
			
		||||
    $private_definition_object = new Definition('\stdClass');
 | 
			
		||||
    $private_definition_object->setPublic(FALSE);
 | 
			
		||||
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'arguments' => ['foo', $private_definition_object],
 | 
			
		||||
      'arguments_count' => 2,
 | 
			
		||||
      'arguments_expected' => static::getCollection([
 | 
			
		||||
        'foo',
 | 
			
		||||
        static::getPrivateServiceCall(NULL, $private_definition),
 | 
			
		||||
      ]),
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test a deep collection without a reference.
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'arguments' => [[['foo']]],
 | 
			
		||||
      'arguments_count' => 1,
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test a deep collection with a reference to resolve.
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'arguments' => [[new Reference('bar')]],
 | 
			
		||||
      'arguments_count' => 1,
 | 
			
		||||
      'arguments_expected' => static::getCollection([static::getCollection([static::getServiceCall('bar')])]),
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test an IteratorArgument collection with a reference to resolve.
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'arguments' => [new IteratorArgument([new Reference('bar')])],
 | 
			
		||||
      'arguments_count' => 1,
 | 
			
		||||
      'arguments_expected' => static::getCollection([static::getIterator([static::getServiceCall('bar')])]),
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test a collection with a variable to resolve.
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'arguments' => [new Parameter('llama_parameter')],
 | 
			
		||||
      'arguments_count' => 1,
 | 
			
		||||
      'arguments_expected' => static::getCollection([static::getParameterCall('llama_parameter')]),
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test getMethodCalls.
 | 
			
		||||
    $calls = [
 | 
			
		||||
      ['method', static::getCollection([])],
 | 
			
		||||
      ['method2', static::getCollection([])],
 | 
			
		||||
    ];
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'calls' => $calls,
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'shared' => FALSE,
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test factory.
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'factory' => [new Reference('bar'), 'factoryMethod'],
 | 
			
		||||
      'factory_expected' => [static::getServiceCall('bar'), 'factoryMethod'],
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test invalid factory - needed to test deep dumpValue().
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'factory' => [['foo', 'llama'], 'factoryMethod'],
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test properties.
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'properties' => ['_value' => 'llama'],
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    // Test configurator.
 | 
			
		||||
    $service_definitions[] = [
 | 
			
		||||
      'configurator' => [new Reference('bar'), 'configureService'],
 | 
			
		||||
      'configurator_expected' => [static::getServiceCall('bar'), 'configureService'],
 | 
			
		||||
    ] + $base_service_definition;
 | 
			
		||||
 | 
			
		||||
    $services_provided = [];
 | 
			
		||||
    $services_provided[] = [
 | 
			
		||||
      [],
 | 
			
		||||
      [],
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    foreach ($service_definitions as $service_definition) {
 | 
			
		||||
      $definition = (new Prophet())->prophesize('\Symfony\Component\DependencyInjection\Definition');
 | 
			
		||||
      $definition->getClass()->willReturn($service_definition['class']);
 | 
			
		||||
      $definition->isPublic()->willReturn($service_definition['public']);
 | 
			
		||||
      $definition->getFile()->willReturn($service_definition['file']);
 | 
			
		||||
      $definition->isSynthetic()->willReturn($service_definition['synthetic']);
 | 
			
		||||
      $definition->isLazy()->willReturn($service_definition['lazy']);
 | 
			
		||||
      $definition->getArguments()->willReturn($service_definition['arguments']);
 | 
			
		||||
      $definition->getProperties()->willReturn($service_definition['properties']);
 | 
			
		||||
      $definition->getMethodCalls()->willReturn($service_definition['calls']);
 | 
			
		||||
      $definition->isShared()->willReturn($service_definition['shared']);
 | 
			
		||||
      $definition->getDecoratedService()->willReturn(NULL);
 | 
			
		||||
      $definition->getFactory()->willReturn($service_definition['factory']);
 | 
			
		||||
      $definition->getConfigurator()->willReturn($service_definition['configurator']);
 | 
			
		||||
 | 
			
		||||
      // Preserve order.
 | 
			
		||||
      $filtered_service_definition = [];
 | 
			
		||||
      foreach ($base_service_definition as $key => $value) {
 | 
			
		||||
        $filtered_service_definition[$key] = $service_definition[$key];
 | 
			
		||||
        unset($service_definition[$key]);
 | 
			
		||||
 | 
			
		||||
        if ($key == 'class' || $key == 'arguments_count') {
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($filtered_service_definition[$key] === $base_service_definition[$key]) {
 | 
			
		||||
          unset($filtered_service_definition[$key]);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Add remaining properties.
 | 
			
		||||
      $filtered_service_definition += $service_definition;
 | 
			
		||||
 | 
			
		||||
      // Allow to set _expected values.
 | 
			
		||||
      foreach (['arguments', 'factory', 'configurator'] as $key) {
 | 
			
		||||
        $expected = $key . '_expected';
 | 
			
		||||
        if (isset($filtered_service_definition[$expected])) {
 | 
			
		||||
          $filtered_service_definition[$key] = $filtered_service_definition[$expected];
 | 
			
		||||
          unset($filtered_service_definition[$expected]);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (isset($filtered_service_definition['public']) && $filtered_service_definition['public'] === FALSE) {
 | 
			
		||||
        $services_provided[] = [
 | 
			
		||||
          ['foo_service' => $definition->reveal()],
 | 
			
		||||
          [],
 | 
			
		||||
        ];
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $services_provided[] = [
 | 
			
		||||
        ['foo_service' => $definition->reveal()],
 | 
			
		||||
        ['foo_service' => static::serializeDefinition($filtered_service_definition)],
 | 
			
		||||
      ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return $services_provided;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper function to serialize a definition.
 | 
			
		||||
   *
 | 
			
		||||
   * Used to override serialization.
 | 
			
		||||
   */
 | 
			
		||||
  protected static function serializeDefinition(array $service_definition) {
 | 
			
		||||
    return serialize($service_definition);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper function to return a service definition.
 | 
			
		||||
   */
 | 
			
		||||
  protected static function getServiceCall($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
 | 
			
		||||
    return (object) [
 | 
			
		||||
      'type' => 'service',
 | 
			
		||||
      'id' => $id,
 | 
			
		||||
      'invalidBehavior' => $invalid_behavior,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper function to return a service closure definition.
 | 
			
		||||
   */
 | 
			
		||||
  protected static function getServiceClosureCall($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
 | 
			
		||||
    return (object) [
 | 
			
		||||
      'type' => 'service_closure',
 | 
			
		||||
      'id' => $id,
 | 
			
		||||
      'invalidBehavior' => $invalid_behavior,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that references to aliases work correctly.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getReferenceCall
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider publicPrivateDataProvider
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetServiceDefinitionWithReferenceToAlias($public): void {
 | 
			
		||||
    $bar_definition = new Definition('\stdClass');
 | 
			
		||||
    $bar_definition_php_array = [
 | 
			
		||||
      'class' => '\stdClass',
 | 
			
		||||
    ];
 | 
			
		||||
    if ($public) {
 | 
			
		||||
      $bar_definition->setPublic(TRUE);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      $bar_definition->setPublic(FALSE);
 | 
			
		||||
      $bar_definition_php_array['public'] = FALSE;
 | 
			
		||||
    }
 | 
			
		||||
    $bar_definition_php_array['arguments_count'] = 0;
 | 
			
		||||
 | 
			
		||||
    $services['bar'] = $bar_definition;
 | 
			
		||||
 | 
			
		||||
    $aliases['bar.alias'] = 'bar';
 | 
			
		||||
 | 
			
		||||
    $foo = new Definition('\stdClass');
 | 
			
		||||
    $foo->setPublic(TRUE);
 | 
			
		||||
    $foo->addArgument(new Reference('bar.alias'));
 | 
			
		||||
 | 
			
		||||
    $services['foo'] = $foo;
 | 
			
		||||
 | 
			
		||||
    $this->containerBuilder->getAliases()->willReturn($aliases);
 | 
			
		||||
    $this->containerBuilder->getDefinitions()->willReturn($services);
 | 
			
		||||
    $this->containerBuilder->getDefinition('bar')->willReturn($bar_definition);
 | 
			
		||||
    $dump = $this->dumper->getArray();
 | 
			
		||||
    if ($public) {
 | 
			
		||||
      $service_definition = static::getServiceCall('bar');
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      $service_definition = static::getPrivateServiceCall('bar', $bar_definition_php_array, TRUE);
 | 
			
		||||
    }
 | 
			
		||||
    $data = [
 | 
			
		||||
      'class' => '\stdClass',
 | 
			
		||||
      'arguments' => static::getCollection([
 | 
			
		||||
        $service_definition,
 | 
			
		||||
      ]),
 | 
			
		||||
      'arguments_count' => 1,
 | 
			
		||||
    ];
 | 
			
		||||
    $this->assertEquals(static::serializeDefinition($data), $dump['services']['foo'], 'Expected definition matches dump.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static function publicPrivateDataProvider() {
 | 
			
		||||
    return [
 | 
			
		||||
      [TRUE],
 | 
			
		||||
      [FALSE],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that getDecoratedService() is unsupported.
 | 
			
		||||
   *
 | 
			
		||||
   * Tests that the correct InvalidArgumentException is thrown for
 | 
			
		||||
   * getDecoratedService().
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getServiceDefinition
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetServiceDefinitionForDecoratedService(): void {
 | 
			
		||||
    $bar_definition = new Definition('\stdClass');
 | 
			
		||||
    $bar_definition->setPublic(TRUE);
 | 
			
		||||
    $bar_definition->setDecoratedService((string) new Reference('foo'));
 | 
			
		||||
    $services['bar'] = $bar_definition;
 | 
			
		||||
 | 
			
		||||
    $this->containerBuilder->getDefinitions()->willReturn($services);
 | 
			
		||||
    $this->expectException(InvalidArgumentException::class);
 | 
			
		||||
    $this->dumper->getArray();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that the correct RuntimeException is thrown for expressions.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::dumpValue
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetServiceDefinitionForExpression(): void {
 | 
			
		||||
    $expression = new Expression('');
 | 
			
		||||
 | 
			
		||||
    $bar_definition = new Definition('\stdClass');
 | 
			
		||||
    $bar_definition->setPublic(TRUE);
 | 
			
		||||
    $bar_definition->addArgument($expression);
 | 
			
		||||
    $services['bar'] = $bar_definition;
 | 
			
		||||
 | 
			
		||||
    $this->containerBuilder->getDefinitions()->willReturn($services);
 | 
			
		||||
    $this->expectException(RuntimeException::class);
 | 
			
		||||
    $this->dumper->getArray();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that the correct RuntimeException is thrown for dumping an object.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::dumpValue
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetServiceDefinitionForObject(): void {
 | 
			
		||||
    $service = new \stdClass();
 | 
			
		||||
 | 
			
		||||
    $bar_definition = new Definition('\stdClass');
 | 
			
		||||
    $bar_definition->setPublic(TRUE);
 | 
			
		||||
    $bar_definition->addArgument($service);
 | 
			
		||||
    $services['bar'] = $bar_definition;
 | 
			
		||||
 | 
			
		||||
    $this->containerBuilder->getDefinitions()->willReturn($services);
 | 
			
		||||
    $this->expectException(RuntimeException::class);
 | 
			
		||||
    $this->dumper->getArray();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that the correct RuntimeException is thrown for dumping a resource.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::dumpValue
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetServiceDefinitionForResource(): void {
 | 
			
		||||
    $resource = fopen('php://memory', 'r');
 | 
			
		||||
 | 
			
		||||
    $bar_definition = new Definition('\stdClass');
 | 
			
		||||
    $bar_definition->setPublic(TRUE);
 | 
			
		||||
    $bar_definition->addArgument($resource);
 | 
			
		||||
    $services['bar'] = $bar_definition;
 | 
			
		||||
 | 
			
		||||
    $this->containerBuilder->getDefinitions()->willReturn($services);
 | 
			
		||||
    $this->expectException(RuntimeException::class);
 | 
			
		||||
    $this->dumper->getArray();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that service arguments with escaped percents are correctly dumped.
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider percentsEscapeProvider
 | 
			
		||||
   */
 | 
			
		||||
  public function testPercentsEscape($expected, $argument): void {
 | 
			
		||||
    $definition = new Definition('\stdClass', [$argument]);
 | 
			
		||||
    $definition->setPublic(TRUE);
 | 
			
		||||
    $this->containerBuilder->getDefinitions()->willReturn([
 | 
			
		||||
      'test' => $definition,
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    $dump = $this->dumper->getArray();
 | 
			
		||||
 | 
			
		||||
    $this->assertEquals(static::serializeDefinition([
 | 
			
		||||
      'class' => '\stdClass',
 | 
			
		||||
      'arguments' => static::getCollection([
 | 
			
		||||
        $this->getRaw($expected),
 | 
			
		||||
      ]),
 | 
			
		||||
      'arguments_count' => 1,
 | 
			
		||||
    ]), $dump['services']['test']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider for testPercentsEscape().
 | 
			
		||||
   *
 | 
			
		||||
   * @return array[]
 | 
			
		||||
   *   Returns data-set elements with:
 | 
			
		||||
   *     - expected final value.
 | 
			
		||||
   *     - escaped value in service definition.
 | 
			
		||||
   */
 | 
			
		||||
  public static function percentsEscapeProvider() {
 | 
			
		||||
    return [
 | 
			
		||||
      ['%foo%', '%%foo%%'],
 | 
			
		||||
      ['foo%bar%', 'foo%%bar%%'],
 | 
			
		||||
      ['%foo%bar', '%%foo%%bar'],
 | 
			
		||||
      ['%', '%'],
 | 
			
		||||
      ['%', '%%'],
 | 
			
		||||
      ['%%', '%%%'],
 | 
			
		||||
      ['%%', '%%%%'],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper function to return a private service definition.
 | 
			
		||||
   */
 | 
			
		||||
  protected static function getPrivateServiceCall($id, $service_definition, $shared = FALSE) {
 | 
			
		||||
    if (!$id) {
 | 
			
		||||
      $hash = Crypt::hashBase64(serialize($service_definition));
 | 
			
		||||
      $id = 'private__' . $hash;
 | 
			
		||||
    }
 | 
			
		||||
    return (object) [
 | 
			
		||||
      'type' => 'private_service',
 | 
			
		||||
      'id' => $id,
 | 
			
		||||
      'value' => $service_definition,
 | 
			
		||||
      'shared' => $shared,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper function to return a machine-optimized collection.
 | 
			
		||||
   */
 | 
			
		||||
  protected static function getCollection($collection) {
 | 
			
		||||
    return (object) [
 | 
			
		||||
      'type' => 'collection',
 | 
			
		||||
      'value' => $collection,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper function to return a machine-optimized iterator.
 | 
			
		||||
   */
 | 
			
		||||
  protected static function getIterator($collection) {
 | 
			
		||||
    return (object) [
 | 
			
		||||
      'type' => 'iterator',
 | 
			
		||||
      'value' => $collection,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper function to return a parameter definition.
 | 
			
		||||
   */
 | 
			
		||||
  protected static function getParameterCall($name) {
 | 
			
		||||
    return (object) [
 | 
			
		||||
      'type' => 'parameter',
 | 
			
		||||
      'name' => $name,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper function to return a raw value definition.
 | 
			
		||||
   */
 | 
			
		||||
  protected function getRaw($value) {
 | 
			
		||||
    return (object) [
 | 
			
		||||
      'type' => 'raw',
 | 
			
		||||
      'value' => $value,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Defines a dummy ExpressionLanguage component.
 | 
			
		||||
 *
 | 
			
		||||
 * As Drupal Core does not ship with ExpressionLanguage component we need to
 | 
			
		||||
 * define a dummy, else it cannot be tested.
 | 
			
		||||
 */
 | 
			
		||||
namespace Symfony\Component\ExpressionLanguage;
 | 
			
		||||
 | 
			
		||||
if (!class_exists('\Symfony\Component\ExpressionLanguage\Expression')) {
 | 
			
		||||
  /**
 | 
			
		||||
   * Dummy class to ensure non-existent Symfony component can be tested.
 | 
			
		||||
   */
 | 
			
		||||
  class Expression {
 | 
			
		||||
 | 
			
		||||
    public function __construct($expression) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the string representation of the expression.
 | 
			
		||||
     */
 | 
			
		||||
    public function __toString() {
 | 
			
		||||
      return 'dummy_expression';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,56 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\DependencyInjection\Dumper;
 | 
			
		||||
 | 
			
		||||
use Symfony\Component\DependencyInjection\ContainerInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\DependencyInjection\Dumper\PhpArrayDumper
 | 
			
		||||
 * @group DependencyInjection
 | 
			
		||||
 */
 | 
			
		||||
class PhpArrayDumperTest extends OptimizedPhpArrayDumperTest {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    $this->machineFormat = FALSE;
 | 
			
		||||
    $this->dumperClass = '\Drupal\Component\DependencyInjection\Dumper\PhpArrayDumper';
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected static function serializeDefinition(array $service_definition): array {
 | 
			
		||||
    return $service_definition;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected static function getServiceCall($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE): string {
 | 
			
		||||
    if ($invalid_behavior !== ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
 | 
			
		||||
      return sprintf('@?%s', $id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return sprintf('@%s', $id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected static function getParameterCall($name): string {
 | 
			
		||||
    return '%' . $name . '%';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected static function getCollection($collection, $resolve = TRUE) {
 | 
			
		||||
    return $collection;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,18 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * Contains a test function for container 'file' include testing.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test function for container testing.
 | 
			
		||||
 *
 | 
			
		||||
 * @return string
 | 
			
		||||
 *   A string just for testing.
 | 
			
		||||
 */
 | 
			
		||||
function container_test_file_service_test_service_function() {
 | 
			
		||||
  return 'Hello Container';
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,52 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\DependencyInjection;
 | 
			
		||||
 | 
			
		||||
use Symfony\Component\DependencyInjection\ContainerInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\DependencyInjection\PhpArrayContainer
 | 
			
		||||
 * @group DependencyInjection
 | 
			
		||||
 */
 | 
			
		||||
class PhpArrayContainerTest extends ContainerTest {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    $this->machineFormat = FALSE;
 | 
			
		||||
    $this->containerClass = '\Drupal\Component\DependencyInjection\PhpArrayContainer';
 | 
			
		||||
    $this->containerDefinition = $this->getMockContainerDefinition();
 | 
			
		||||
    $this->container = new $this->containerClass($this->containerDefinition);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper function to return a service definition.
 | 
			
		||||
   */
 | 
			
		||||
  protected function getServiceCall($id, $invalid_behavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE): string {
 | 
			
		||||
    if ($invalid_behavior !== ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) {
 | 
			
		||||
      return sprintf('@?%s', $id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return sprintf('@%s', $id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper function to return a service definition.
 | 
			
		||||
   */
 | 
			
		||||
  protected function getParameterCall($name): string {
 | 
			
		||||
    return '%' . $name . '%';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper function to return a machine-optimized '@notation'.
 | 
			
		||||
   */
 | 
			
		||||
  protected function getCollection($collection, $resolve = TRUE) {
 | 
			
		||||
    return $collection;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,62 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\DependencyInjection;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\DependencyInjection\ReverseContainer;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests the ReverseContainer class.
 | 
			
		||||
 *
 | 
			
		||||
 * The reverse container uses a static to maintain information across
 | 
			
		||||
 * container rebuilds.
 | 
			
		||||
 *
 | 
			
		||||
 * @runTestsInSeparateProcesses
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\DependencyInjection\ReverseContainer
 | 
			
		||||
 * @group DependencyInjection
 | 
			
		||||
 */
 | 
			
		||||
class ReverseContainerTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getId
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetId(): void {
 | 
			
		||||
    $container = new ContainerBuilder();
 | 
			
		||||
    $service = new \stdClass();
 | 
			
		||||
    $container->set('bar', $service);
 | 
			
		||||
 | 
			
		||||
    $reverse_container = new ReverseContainer($container);
 | 
			
		||||
 | 
			
		||||
    $this->assertSame('bar', $reverse_container->getId($service));
 | 
			
		||||
    $non_service = new \stdClass();
 | 
			
		||||
    $this->assertNull($reverse_container->getId($non_service));
 | 
			
		||||
    $this->assertSame('service_container', $reverse_container->getId($container));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::recordContainer
 | 
			
		||||
   */
 | 
			
		||||
  public function testRecordContainer(): void {
 | 
			
		||||
    $container = new ContainerBuilder();
 | 
			
		||||
    $service = new \stdClass();
 | 
			
		||||
    $container->set('bar', $service);
 | 
			
		||||
 | 
			
		||||
    $reverse_container = new ReverseContainer($container);
 | 
			
		||||
    $reverse_container->recordContainer();
 | 
			
		||||
 | 
			
		||||
    $container = new ContainerBuilder();
 | 
			
		||||
    $reverse_container = new ReverseContainer($container);
 | 
			
		||||
 | 
			
		||||
    // New container does not have a bar service.
 | 
			
		||||
    $this->assertNull($reverse_container->getId($service));
 | 
			
		||||
 | 
			
		||||
    // Add the bar service to make the lookup based on the old object work as
 | 
			
		||||
    // expected.
 | 
			
		||||
    $container->set('bar', new \stdClass());
 | 
			
		||||
    $this->assertSame('bar', $reverse_container->getId($service));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,60 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Diff;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Diff\Diff;
 | 
			
		||||
use Drupal\Component\Diff\DiffFormatter;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test DiffFormatter classes.
 | 
			
		||||
 *
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Diff\DiffFormatter
 | 
			
		||||
 *
 | 
			
		||||
 * @group Diff
 | 
			
		||||
 */
 | 
			
		||||
class DiffFormatterTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   - Expected formatted diff output.
 | 
			
		||||
   *   - First array of text to diff.
 | 
			
		||||
   *   - Second array of text to diff.
 | 
			
		||||
   */
 | 
			
		||||
  public static function provideTestDiff() {
 | 
			
		||||
    return [
 | 
			
		||||
      'empty' => ['', [], []],
 | 
			
		||||
      'add' => [
 | 
			
		||||
        "3a3\n> line2a\n",
 | 
			
		||||
        ['line1', 'line2', 'line3'],
 | 
			
		||||
        ['line1', 'line2', 'line2a', 'line3'],
 | 
			
		||||
      ],
 | 
			
		||||
      'delete' => [
 | 
			
		||||
        "3d3\n< line2a\n",
 | 
			
		||||
        ['line1', 'line2', 'line2a', 'line3'],
 | 
			
		||||
        ['line1', 'line2', 'line3'],
 | 
			
		||||
      ],
 | 
			
		||||
      'change' => [
 | 
			
		||||
        "3c3\n< line2a\n---\n> line2b\n",
 | 
			
		||||
        ['line1', 'line2', 'line2a', 'line3'],
 | 
			
		||||
        ['line1', 'line2', 'line2b', 'line3'],
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests whether op classes returned by DiffEngine::diff() match expectations.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::format
 | 
			
		||||
   * @dataProvider provideTestDiff
 | 
			
		||||
   */
 | 
			
		||||
  public function testDiff($expected, $from, $to): void {
 | 
			
		||||
    $diff = new Diff($from, $to);
 | 
			
		||||
    $formatter = new DiffFormatter();
 | 
			
		||||
    $output = $formatter->format($diff);
 | 
			
		||||
    $this->assertEquals($expected, $output);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,133 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Diff;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Diff\DiffOpOutputBuilder;
 | 
			
		||||
use Drupal\Component\Diff\Engine\DiffOpAdd;
 | 
			
		||||
use Drupal\Component\Diff\Engine\DiffOpCopy;
 | 
			
		||||
use Drupal\Component\Diff\Engine\DiffOpChange;
 | 
			
		||||
use Drupal\Component\Diff\Engine\DiffOpDelete;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use SebastianBergmann\Diff\Differ;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Diff\DiffOpOutputBuilder
 | 
			
		||||
 *
 | 
			
		||||
 * @group Diff
 | 
			
		||||
 */
 | 
			
		||||
class DiffOpOutputBuilderTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   - Expected output in terms of return class. A list of class names
 | 
			
		||||
   *     expected to be returned by DiffEngine::diff().
 | 
			
		||||
   *   - An array of strings to change from.
 | 
			
		||||
   *   - An array of strings to change to.
 | 
			
		||||
   */
 | 
			
		||||
  public static function provideTestDiff(): array {
 | 
			
		||||
    return [
 | 
			
		||||
      'empty' => [[], [], []],
 | 
			
		||||
      'add' => [[new DiffOpAdd(['a'])], [], ['a']],
 | 
			
		||||
      'copy' => [[new DiffOpCopy(['a'])], ['a'], ['a']],
 | 
			
		||||
      'change' => [[new DiffOpChange(['a'], ['b'])], ['a'], ['b']],
 | 
			
		||||
      'copy-and-change' => [
 | 
			
		||||
        [
 | 
			
		||||
          new DiffOpCopy(['a']),
 | 
			
		||||
          new DiffOpChange(['b'], ['c']),
 | 
			
		||||
        ],
 | 
			
		||||
        ['a', 'b'],
 | 
			
		||||
        ['a', 'c'],
 | 
			
		||||
      ],
 | 
			
		||||
      'copy-change-copy' => [
 | 
			
		||||
        [
 | 
			
		||||
          new DiffOpCopy(['a']),
 | 
			
		||||
          new DiffOpChange(['b'], ['c']),
 | 
			
		||||
          new DiffOpCopy(['d']),
 | 
			
		||||
        ],
 | 
			
		||||
        ['a', 'b', 'd'],
 | 
			
		||||
        ['a', 'c', 'd'],
 | 
			
		||||
      ],
 | 
			
		||||
      'copy-change-copy-add' => [
 | 
			
		||||
        [
 | 
			
		||||
          new DiffOpCopy(['a']),
 | 
			
		||||
          new DiffOpChange(['b'], ['c']),
 | 
			
		||||
          new DiffOpCopy(['d']),
 | 
			
		||||
          new DiffOpAdd(['e']),
 | 
			
		||||
        ],
 | 
			
		||||
        ['a', 'b', 'd'],
 | 
			
		||||
        ['a', 'c', 'd', 'e'],
 | 
			
		||||
      ],
 | 
			
		||||
      'copy-delete' => [
 | 
			
		||||
        [
 | 
			
		||||
          new DiffOpCopy(['a']),
 | 
			
		||||
          new DiffOpDelete(['b', 'd']),
 | 
			
		||||
        ],
 | 
			
		||||
        ['a', 'b', 'd'],
 | 
			
		||||
        ['a'],
 | 
			
		||||
      ],
 | 
			
		||||
      'change-copy' => [
 | 
			
		||||
        [
 | 
			
		||||
          new DiffOpChange(['aa', 'bb', 'cc'], ['a', 'c']),
 | 
			
		||||
          new DiffOpCopy(['d']),
 | 
			
		||||
        ],
 | 
			
		||||
        ['aa', 'bb', 'cc', 'd'],
 | 
			
		||||
        ['a', 'c', 'd'],
 | 
			
		||||
      ],
 | 
			
		||||
      'copy-change-copy-change' => [
 | 
			
		||||
        [
 | 
			
		||||
          new DiffOpCopy(['a']),
 | 
			
		||||
          new DiffOpChange(['bb'], ['b', 'c']),
 | 
			
		||||
          new DiffOpCopy(['d']),
 | 
			
		||||
          new DiffOpChange(['ee'], ['e']),
 | 
			
		||||
        ],
 | 
			
		||||
        ['a', 'bb', 'd', 'ee'],
 | 
			
		||||
        ['a', 'b', 'c', 'd', 'e'],
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests whether op classes returned match expectations.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::toOpsArray
 | 
			
		||||
   * @dataProvider provideTestDiff
 | 
			
		||||
   */
 | 
			
		||||
  public function testToOpsArray(array $expected, array $from, array $to): void {
 | 
			
		||||
    $diffOpBuilder = new DiffOpOutputBuilder();
 | 
			
		||||
    $differ = new Differ($diffOpBuilder);
 | 
			
		||||
    $diff = $differ->diffToArray($from, $to);
 | 
			
		||||
    $this->assertEquals($expected, $diffOpBuilder->toOpsArray($diff));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getDiff
 | 
			
		||||
   * @dataProvider provideTestDiff
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetDiff(array $expected, array $from, array $to): void {
 | 
			
		||||
    $differ = new Differ(new DiffOpOutputBuilder());
 | 
			
		||||
    $diff = $differ->diff($from, $to);
 | 
			
		||||
    $this->assertEquals($expected, unserialize($diff));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that two files can be successfully diffed.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::toOpsArray
 | 
			
		||||
   */
 | 
			
		||||
  public function testDiffInfiniteLoop(): void {
 | 
			
		||||
    $from = explode("\n", file_get_contents(__DIR__ . '/Engine/fixtures/file1.txt'));
 | 
			
		||||
    $to = explode("\n", file_get_contents(__DIR__ . '/Engine/fixtures/file2.txt'));
 | 
			
		||||
    $diffOpBuilder = new DiffOpOutputBuilder();
 | 
			
		||||
    $differ = new Differ($diffOpBuilder);
 | 
			
		||||
    $diff = $differ->diffToArray($from, $to);
 | 
			
		||||
    $diffOps = $diffOpBuilder->toOpsArray($diff);
 | 
			
		||||
    $this->assertCount(4, $diffOps);
 | 
			
		||||
    $this->assertEquals($diffOps[0], new DiffOpAdd(['    - image.style.max_325x325']));
 | 
			
		||||
    $this->assertEquals($diffOps[1], new DiffOpCopy(['    - image.style.max_650x650']));
 | 
			
		||||
    $this->assertEquals($diffOps[2], new DiffOpChange(['    - image.style.max_325x325'], ['_core:', '  default_config_hash: random_hash_string_here']));
 | 
			
		||||
    $this->assertEquals($diffOps[3], new DiffOpCopy(['fallback_image_style: max_325x325', '']));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,59 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Diff\Engine;
 | 
			
		||||
 | 
			
		||||
// cspell:ignore HWLDFWordAccumulator
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Diff\Engine\HWLDFWordAccumulator;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
// cspell:ignore wordword
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test HWLDFWordAccumulator.
 | 
			
		||||
 *
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Diff\Engine\HWLDFWordAccumulator
 | 
			
		||||
 *
 | 
			
		||||
 * @group Diff
 | 
			
		||||
 */
 | 
			
		||||
class HWLDFWordAccumulatorTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Verify that we only get back a NBSP from an empty accumulator.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getLines
 | 
			
		||||
   *
 | 
			
		||||
   * @see Drupal\Component\Diff\Engine\HWLDFWordAccumulator::NBSP
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetLinesEmpty(): void {
 | 
			
		||||
    $acc = new HWLDFWordAccumulator();
 | 
			
		||||
    $this->assertEquals([' '], $acc->getLines());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   - Expected array of lines from getLines().
 | 
			
		||||
   *   - Array of strings for the $words parameter to addWords().
 | 
			
		||||
   *   - String tag for the $tag parameter to addWords().
 | 
			
		||||
   */
 | 
			
		||||
  public static function provideAddWords() {
 | 
			
		||||
    return [
 | 
			
		||||
      [['wordword2'], ['word', 'word2'], 'tag'],
 | 
			
		||||
      [['word', 'word2'], ['word', "\nword2"], 'tag'],
 | 
			
		||||
      [[' ', 'word2'], ['', "\nword2"], 'tag'],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::addWords
 | 
			
		||||
   * @dataProvider provideAddWords
 | 
			
		||||
   */
 | 
			
		||||
  public function testAddWords($expected, $words, $tag): void {
 | 
			
		||||
    $acc = new HWLDFWordAccumulator();
 | 
			
		||||
    $acc->addWords($words, $tag);
 | 
			
		||||
    $this->assertEquals($expected, $acc->getLines());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,3 @@
 | 
			
		||||
    - image.style.max_650x650
 | 
			
		||||
    - image.style.max_325x325
 | 
			
		||||
fallback_image_style: max_325x325
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
    - image.style.max_325x325
 | 
			
		||||
    - image.style.max_650x650
 | 
			
		||||
_core:
 | 
			
		||||
  default_config_hash: random_hash_string_here
 | 
			
		||||
fallback_image_style: max_325x325
 | 
			
		||||
@ -0,0 +1,167 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Discovery;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Discovery\DiscoveryException;
 | 
			
		||||
use Drupal\Component\Discovery\YamlDirectoryDiscovery;
 | 
			
		||||
use Drupal\Component\FileCache\FileCacheFactory;
 | 
			
		||||
use org\bovigo\vfs\vfsStream;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * YamlDirectoryDiscoveryTest component unit tests.
 | 
			
		||||
 *
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Discovery\YamlDirectoryDiscovery
 | 
			
		||||
 *
 | 
			
		||||
 * @group Discovery
 | 
			
		||||
 */
 | 
			
		||||
class YamlDirectoryDiscoveryTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    // Ensure that FileCacheFactory has a prefix.
 | 
			
		||||
    FileCacheFactory::setPrefix('prefix');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests YAML directory discovery.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::findAll
 | 
			
		||||
   */
 | 
			
		||||
  public function testDiscovery(): void {
 | 
			
		||||
    vfsStream::setup('modules', NULL, [
 | 
			
		||||
      'test_1' => [
 | 
			
		||||
        'subdir1' => [
 | 
			
		||||
          'item_1.test.yml' => "id: item1\nname: 'test1 item 1'",
 | 
			
		||||
        ],
 | 
			
		||||
        'subdir2' => [
 | 
			
		||||
          'item_2.test.yml' => "id: item2\nname: 'test1 item 2'",
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      'test_2' => [
 | 
			
		||||
        'subdir1' => [
 | 
			
		||||
          'item_3.test.yml' => "id: item3\nname: 'test2 item 3'",
 | 
			
		||||
        ],
 | 
			
		||||
        'subdir2' => [],
 | 
			
		||||
      ],
 | 
			
		||||
      'test_3' => [],
 | 
			
		||||
      'test_4' => [
 | 
			
		||||
        'subdir1' => [
 | 
			
		||||
          'item_4.test.yml' => "id: item4\nname: 'test4 item 4'",
 | 
			
		||||
          'item_5.test.yml' => "id: item5\nname: 'test4 item 5'",
 | 
			
		||||
          'item_6.test.yml' => "id: item6\nname: 'test4 item 6'",
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // Set up the directories to search.
 | 
			
		||||
    $directories = [
 | 
			
		||||
      // Multiple directories both with valid items.
 | 
			
		||||
      'test_1' => [
 | 
			
		||||
        vfsStream::url('modules/test_1/subdir1'),
 | 
			
		||||
        vfsStream::url('modules/test_1/subdir2'),
 | 
			
		||||
      ],
 | 
			
		||||
      // The subdir2 directory is empty.
 | 
			
		||||
      'test_2' => [
 | 
			
		||||
        vfsStream::url('modules/test_2/subdir1'),
 | 
			
		||||
        vfsStream::url('modules/test_2/subdir2'),
 | 
			
		||||
      ],
 | 
			
		||||
      // Directories that do not exist.
 | 
			
		||||
      'test_3' => [
 | 
			
		||||
        vfsStream::url('modules/test_3/subdir1'),
 | 
			
		||||
        vfsStream::url('modules/test_3/subdir2'),
 | 
			
		||||
      ],
 | 
			
		||||
      // A single directory.
 | 
			
		||||
      'test_4' => vfsStream::url('modules/test_4/subdir1'),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    $discovery = new YamlDirectoryDiscovery($directories, 'test');
 | 
			
		||||
    $data = $discovery->findAll();
 | 
			
		||||
 | 
			
		||||
    // The file path is dependent on the operating system, so we adjust the
 | 
			
		||||
    // directory separator.
 | 
			
		||||
    $this->assertSame(['id' => 'item1', 'name' => 'test1 item 1', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_1/subdir1' . DIRECTORY_SEPARATOR . 'item_1.test.yml'], $data['test_1']['item1']);
 | 
			
		||||
    $this->assertSame(['id' => 'item2', 'name' => 'test1 item 2', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_1/subdir2' . DIRECTORY_SEPARATOR . 'item_2.test.yml'], $data['test_1']['item2']);
 | 
			
		||||
    $this->assertCount(2, $data['test_1']);
 | 
			
		||||
 | 
			
		||||
    $this->assertSame(['id' => 'item3', 'name' => 'test2 item 3', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_2/subdir1' . DIRECTORY_SEPARATOR . 'item_3.test.yml'], $data['test_2']['item3']);
 | 
			
		||||
    $this->assertCount(1, $data['test_2']);
 | 
			
		||||
 | 
			
		||||
    $this->assertArrayNotHasKey('test_3', $data, 'test_3 provides 0 items');
 | 
			
		||||
 | 
			
		||||
    $this->assertSame(['id' => 'item4', 'name' => 'test4 item 4', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_4/subdir1' . DIRECTORY_SEPARATOR . 'item_4.test.yml'], $data['test_4']['item4']);
 | 
			
		||||
    $this->assertSame(['id' => 'item5', 'name' => 'test4 item 5', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_4/subdir1' . DIRECTORY_SEPARATOR . 'item_5.test.yml'], $data['test_4']['item5']);
 | 
			
		||||
    $this->assertSame(['id' => 'item6', 'name' => 'test4 item 6', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_4/subdir1' . DIRECTORY_SEPARATOR . 'item_6.test.yml'], $data['test_4']['item6']);
 | 
			
		||||
    $this->assertCount(3, $data['test_4']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests YAML directory discovery with an alternate ID key.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::findAll
 | 
			
		||||
   */
 | 
			
		||||
  public function testDiscoveryAlternateId(): void {
 | 
			
		||||
    vfsStream::setup('modules', NULL, [
 | 
			
		||||
      'test_1' => [
 | 
			
		||||
        'item_1.test.yml' => "alt_id: item1\nid: ignored",
 | 
			
		||||
      ],
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // Set up the directories to search.
 | 
			
		||||
    $directories = ['test_1' => vfsStream::url('modules/test_1')];
 | 
			
		||||
 | 
			
		||||
    $discovery = new YamlDirectoryDiscovery($directories, 'test', 'alt_id');
 | 
			
		||||
    $data = $discovery->findAll();
 | 
			
		||||
 | 
			
		||||
    $this->assertSame(['alt_id' => 'item1', 'id' => 'ignored', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_1' . DIRECTORY_SEPARATOR . 'item_1.test.yml'], $data['test_1']['item1']);
 | 
			
		||||
    $this->assertCount(1, $data['test_1']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests YAML directory discovery with a missing ID key.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::findAll
 | 
			
		||||
   * @covers ::getIdentifier
 | 
			
		||||
   */
 | 
			
		||||
  public function testDiscoveryNoIdException(): void {
 | 
			
		||||
    $this->expectException(DiscoveryException::class);
 | 
			
		||||
    $this->expectExceptionMessage('The vfs://modules/test_1' . DIRECTORY_SEPARATOR . 'item_1.test.yml contains no data in the identifier key \'id\'');
 | 
			
		||||
    vfsStream::setup('modules', NULL, [
 | 
			
		||||
      'test_1' => [
 | 
			
		||||
        'item_1.test.yml' => "",
 | 
			
		||||
      ],
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // Set up the directories to search.
 | 
			
		||||
    $directories = ['test_1' => vfsStream::url('modules/test_1')];
 | 
			
		||||
 | 
			
		||||
    $discovery = new YamlDirectoryDiscovery($directories, 'test');
 | 
			
		||||
    $discovery->findAll();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests YAML directory discovery with invalid YAML.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::findAll
 | 
			
		||||
   */
 | 
			
		||||
  public function testDiscoveryInvalidYamlException(): void {
 | 
			
		||||
    $this->expectException(DiscoveryException::class);
 | 
			
		||||
    $this->expectExceptionMessage('The vfs://modules/test_1' . DIRECTORY_SEPARATOR . 'item_1.test.yml contains invalid YAML');
 | 
			
		||||
    vfsStream::setup('modules', NULL, [
 | 
			
		||||
      'test_1' => [
 | 
			
		||||
        'item_1.test.yml' => "id: invalid\nfoo : [bar}",
 | 
			
		||||
      ],
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // Set up the directories to search.
 | 
			
		||||
    $directories = ['test_1' => vfsStream::url('modules/test_1')];
 | 
			
		||||
 | 
			
		||||
    $discovery = new YamlDirectoryDiscovery($directories, 'test');
 | 
			
		||||
    $discovery->findAll();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,93 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Discovery;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Discovery\YamlDiscovery;
 | 
			
		||||
use Drupal\Component\FileCache\FileCacheFactory;
 | 
			
		||||
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
 | 
			
		||||
use org\bovigo\vfs\vfsStream;
 | 
			
		||||
use org\bovigo\vfs\vfsStreamDirectory;
 | 
			
		||||
use org\bovigo\vfs\vfsStreamWrapper;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * YamlDiscovery component unit tests.
 | 
			
		||||
 *
 | 
			
		||||
 * @group Discovery
 | 
			
		||||
 */
 | 
			
		||||
class YamlDiscoveryTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    // Ensure that FileCacheFactory has a prefix.
 | 
			
		||||
    FileCacheFactory::setPrefix('prefix');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests the YAML file discovery.
 | 
			
		||||
   */
 | 
			
		||||
  public function testDiscovery(): void {
 | 
			
		||||
    vfsStreamWrapper::register();
 | 
			
		||||
    $root = new vfsStreamDirectory('modules');
 | 
			
		||||
    vfsStreamWrapper::setRoot($root);
 | 
			
		||||
    $url = vfsStream::url('modules');
 | 
			
		||||
 | 
			
		||||
    mkdir($url . '/test_1');
 | 
			
		||||
    file_put_contents($url . '/test_1/test_1.test.yml', 'name: test');
 | 
			
		||||
    file_put_contents($url . '/test_1/test_2.test.yml', 'name: test');
 | 
			
		||||
 | 
			
		||||
    mkdir($url . '/test_2');
 | 
			
		||||
    file_put_contents($url . '/test_2/test_3.test.yml', 'name: test');
 | 
			
		||||
    // Write an empty YAML file.
 | 
			
		||||
    file_put_contents($url . '/test_2/test_4.test.yml', '');
 | 
			
		||||
 | 
			
		||||
    // Set up the directories to search.
 | 
			
		||||
    $directories = [
 | 
			
		||||
      'test_1' => $url . '/test_1',
 | 
			
		||||
      'test_2' => $url . '/test_1',
 | 
			
		||||
      'test_3' => $url . '/test_2',
 | 
			
		||||
      'test_4' => $url . '/test_2',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    $discovery = new YamlDiscovery('test', $directories);
 | 
			
		||||
    $data = $discovery->findAll();
 | 
			
		||||
 | 
			
		||||
    $this->assertCount(4, $data);
 | 
			
		||||
    $this->assertArrayHasKey('test_1', $data);
 | 
			
		||||
    $this->assertArrayHasKey('test_2', $data);
 | 
			
		||||
    $this->assertArrayHasKey('test_3', $data);
 | 
			
		||||
    $this->assertArrayHasKey('test_4', $data);
 | 
			
		||||
 | 
			
		||||
    foreach (['test_1', 'test_2', 'test_3'] as $key) {
 | 
			
		||||
      $this->assertArrayHasKey('name', $data[$key]);
 | 
			
		||||
      $this->assertEquals('test', $data[$key]['name']);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $this->assertSame([], $data['test_4']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests if filename is output for a broken YAML file.
 | 
			
		||||
   */
 | 
			
		||||
  public function testForBrokenYml(): void {
 | 
			
		||||
    vfsStreamWrapper::register();
 | 
			
		||||
    $root = new vfsStreamDirectory('modules');
 | 
			
		||||
    vfsStreamWrapper::setRoot($root);
 | 
			
		||||
    $url = vfsStream::url('modules');
 | 
			
		||||
 | 
			
		||||
    mkdir($url . '/test_broken');
 | 
			
		||||
    file_put_contents($url . '/test_broken/test_broken.test.yml', "broken:\n:");
 | 
			
		||||
 | 
			
		||||
    $this->expectException(InvalidDataTypeException::class);
 | 
			
		||||
    $this->expectExceptionMessage('vfs://modules/test_broken/test_broken.test.yml');
 | 
			
		||||
 | 
			
		||||
    $directories = ['test_broken' => $url . '/test_broken'];
 | 
			
		||||
    $discovery = new YamlDiscovery('test', $directories);
 | 
			
		||||
    $discovery->findAll();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										163
									
								
								web/core/tests/Drupal/Tests/Component/DrupalComponentTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								web/core/tests/Drupal/Tests/Component/DrupalComponentTest.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,163 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component;
 | 
			
		||||
 | 
			
		||||
use org\bovigo\vfs\vfsStream;
 | 
			
		||||
use PHPUnit\Framework\AssertionFailedError;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * General tests for \Drupal\Component that can't go anywhere else.
 | 
			
		||||
 *
 | 
			
		||||
 * @group Component
 | 
			
		||||
 */
 | 
			
		||||
class DrupalComponentTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that classes in Component do not use any Core class.
 | 
			
		||||
   */
 | 
			
		||||
  public function testNoCoreInComponent(): void {
 | 
			
		||||
    $component_path = dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))) . '/lib/Drupal/Component';
 | 
			
		||||
    foreach ($this->findPhpClasses($component_path) as $class) {
 | 
			
		||||
      $this->assertNoCoreUsage($class);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that classes in Component Tests do not use any Core class.
 | 
			
		||||
   */
 | 
			
		||||
  public function testNoCoreInComponentTests(): void {
 | 
			
		||||
    $component_path = dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))) . '/tests/Drupal/Tests/Component';
 | 
			
		||||
    foreach ($this->findPhpClasses($component_path) as $class) {
 | 
			
		||||
      $this->assertNoCoreUsage($class);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests LICENSE.txt is present and has the correct content.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $component_path
 | 
			
		||||
   *   The path to the component.
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider getComponents
 | 
			
		||||
   */
 | 
			
		||||
  public function testComponentLicense(string $component_path): void {
 | 
			
		||||
    $this->assertFileExists($component_path . DIRECTORY_SEPARATOR . 'LICENSE.txt');
 | 
			
		||||
    $this->assertSame('e84dac1d9fbb5a4a69e38654ce644cea769aa76b', hash_file('sha1', $component_path . DIRECTORY_SEPARATOR . 'LICENSE.txt'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An associative array where the keys are component names and the values
 | 
			
		||||
   *   are arrays containing the corresponding component path.
 | 
			
		||||
   */
 | 
			
		||||
  public static function getComponents(): array {
 | 
			
		||||
    $root_component_path = dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))) . '/lib/Drupal/Component';
 | 
			
		||||
    $component_paths = [];
 | 
			
		||||
    foreach (new \DirectoryIterator($root_component_path) as $file) {
 | 
			
		||||
      if ($file->isDir() && !$file->isDot()) {
 | 
			
		||||
        $component_paths[$file->getBasename()] = [$file->getPathname()];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return $component_paths;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Searches a directory recursively for PHP classes.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $dir
 | 
			
		||||
   *   The full path to the directory that should be checked.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of class paths.
 | 
			
		||||
   */
 | 
			
		||||
  protected function findPhpClasses($dir): array {
 | 
			
		||||
    $classes = [];
 | 
			
		||||
    foreach (new \DirectoryIterator($dir) as $file) {
 | 
			
		||||
      if ($file->isDir() && !$file->isDot()) {
 | 
			
		||||
        $classes = array_merge($classes, $this->findPhpClasses($file->getPathname()));
 | 
			
		||||
      }
 | 
			
		||||
      elseif ($file->getExtension() == 'php') {
 | 
			
		||||
        $classes[] = $file->getPathname();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return $classes;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Asserts that the given class is not using any class from Core namespace.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $class_path
 | 
			
		||||
   *   The full path to the class that should be checked.
 | 
			
		||||
   *
 | 
			
		||||
   * @internal
 | 
			
		||||
   */
 | 
			
		||||
  protected function assertNoCoreUsage(string $class_path): void {
 | 
			
		||||
    $contents = file_get_contents($class_path);
 | 
			
		||||
    preg_match_all('/^.*Drupal\\\Core.*$/m', $contents, $matches);
 | 
			
		||||
    $matches = array_filter($matches[0], function ($line) {
 | 
			
		||||
      // Filter references that don't really matter.
 | 
			
		||||
      return preg_match('/@see|E_USER_DEPRECATED|expectDeprecation/', $line) === 0;
 | 
			
		||||
    });
 | 
			
		||||
    $this->assertEmpty($matches, "Checking for illegal reference to 'Drupal\\Core' namespace in $class_path");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider for testAssertNoCoreUsage().
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   Data for testAssertNoCoreUsage() in the form:
 | 
			
		||||
   *   - TRUE if the test passes, FALSE otherwise.
 | 
			
		||||
   *   - File data as a string. This will be used as a virtual file.
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerAssertNoCoreUsage() {
 | 
			
		||||
    return [
 | 
			
		||||
      [
 | 
			
		||||
        TRUE,
 | 
			
		||||
        '@see \\Drupal\\Core\\Something',
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        FALSE,
 | 
			
		||||
        '\\Drupal\\Core\\Something',
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        FALSE,
 | 
			
		||||
        "@see \\Drupal\\Core\\Something\n" .
 | 
			
		||||
        '\\Drupal\\Core\\Something',
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        FALSE,
 | 
			
		||||
        "\\Drupal\\Core\\Something\n" .
 | 
			
		||||
        '@see \\Drupal\\Core\\Something',
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers \Drupal\Tests\Component\DrupalComponentTest::assertNoCoreUsage
 | 
			
		||||
   * @dataProvider providerAssertNoCoreUsage
 | 
			
		||||
   */
 | 
			
		||||
  public function testAssertNoCoreUsage($expected_pass, $file_data): void {
 | 
			
		||||
    // Set up a virtual file to read.
 | 
			
		||||
    $vfs_root = vfsStream::setup('root');
 | 
			
		||||
    vfsStream::newFile('Test.php')->at($vfs_root)->setContent($file_data);
 | 
			
		||||
    $file_uri = vfsStream::url('root/Test.php');
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      $pass = TRUE;
 | 
			
		||||
      $this->assertNoCoreUsage($file_uri);
 | 
			
		||||
    }
 | 
			
		||||
    catch (AssertionFailedError) {
 | 
			
		||||
      $pass = FALSE;
 | 
			
		||||
    }
 | 
			
		||||
    $this->assertEquals($expected_pass, $pass, $expected_pass ?
 | 
			
		||||
      'Test caused a false positive' :
 | 
			
		||||
      'Test failed to detect Core usage');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,186 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\FileCache;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\FileCache\FileCache;
 | 
			
		||||
use Drupal\Component\FileCache\NullFileCache;
 | 
			
		||||
use Drupal\Component\FileCache\FileCacheFactory;
 | 
			
		||||
use Drupal\Component\Utility\Random;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\FileCache\FileCacheFactory
 | 
			
		||||
 * @group FileCache
 | 
			
		||||
 */
 | 
			
		||||
class FileCacheFactoryTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    $configuration = [
 | 
			
		||||
      'test_foo_settings' => [
 | 
			
		||||
        'collection' => 'test-23',
 | 
			
		||||
        'cache_backend_class' => '\Drupal\Tests\Component\FileCache\StaticFileCacheBackend',
 | 
			
		||||
        'cache_backend_configuration' => [
 | 
			
		||||
          'bin' => 'dog',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
    FileCacheFactory::setConfiguration($configuration);
 | 
			
		||||
    FileCacheFactory::setPrefix('prefix');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::get
 | 
			
		||||
   */
 | 
			
		||||
  public function testGet(): void {
 | 
			
		||||
    $file_cache = FileCacheFactory::get('test_foo_settings', []);
 | 
			
		||||
 | 
			
		||||
    // Ensure the right backend and configuration is used.
 | 
			
		||||
    $filename = __DIR__ . '/Fixtures/llama-23.txt';
 | 
			
		||||
    $realpath = realpath($filename);
 | 
			
		||||
    $cid = 'prefix:test-23:' . $realpath;
 | 
			
		||||
 | 
			
		||||
    $file_cache->set($filename, 23);
 | 
			
		||||
 | 
			
		||||
    $static_cache = new StaticFileCacheBackend(['bin' => 'dog']);
 | 
			
		||||
    $result = $static_cache->fetch([$cid]);
 | 
			
		||||
    $this->assertNotEmpty($result);
 | 
			
		||||
 | 
			
		||||
    // Cleanup static caches.
 | 
			
		||||
    $file_cache->delete($filename);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::get
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetNoPrefix(): void {
 | 
			
		||||
    FileCacheFactory::setPrefix(NULL);
 | 
			
		||||
    $this->expectException(\InvalidArgumentException::class);
 | 
			
		||||
    $this->expectExceptionMessage('Required prefix configuration is missing');
 | 
			
		||||
    FileCacheFactory::get('test_foo_settings', []);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::get
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetDisabledFileCache(): void {
 | 
			
		||||
    // Ensure the returned FileCache is an instance of FileCache::class.
 | 
			
		||||
    $file_cache = FileCacheFactory::get('test_foo_settings', []);
 | 
			
		||||
    $this->assertInstanceOf(FileCache::class, $file_cache);
 | 
			
		||||
 | 
			
		||||
    $configuration = FileCacheFactory::getConfiguration();
 | 
			
		||||
    $configuration[FileCacheFactory::DISABLE_CACHE] = TRUE;
 | 
			
		||||
    FileCacheFactory::setConfiguration($configuration);
 | 
			
		||||
 | 
			
		||||
    // Ensure the returned FileCache is now an instance of NullFileCache::class.
 | 
			
		||||
    $file_cache = FileCacheFactory::get('test_foo_settings', []);
 | 
			
		||||
    $this->assertInstanceOf(NullFileCache::class, $file_cache);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::get
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider configurationDataProvider
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetConfigurationOverrides($configuration, $arguments, $class): void {
 | 
			
		||||
    FileCacheFactory::setConfiguration($configuration);
 | 
			
		||||
 | 
			
		||||
    $file_cache = FileCacheFactory::get('test_foo_settings', $arguments);
 | 
			
		||||
    $this->assertInstanceOf($class, $file_cache);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider for testGetConfigurationOverrides().
 | 
			
		||||
   */
 | 
			
		||||
  public static function configurationDataProvider() {
 | 
			
		||||
    $data = [];
 | 
			
		||||
 | 
			
		||||
    // Test fallback configuration.
 | 
			
		||||
    $data['fallback-configuration'] = [
 | 
			
		||||
      [],
 | 
			
		||||
      [],
 | 
			
		||||
      FileCache::class,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    // Test default configuration.
 | 
			
		||||
    $data['default-configuration'] = [
 | 
			
		||||
      ['default' => ['class' => CustomFileCache::class]],
 | 
			
		||||
      [],
 | 
			
		||||
      CustomFileCache::class,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    // Test specific per collection setting.
 | 
			
		||||
    $data['collection-setting'] = [
 | 
			
		||||
      ['test_foo_settings' => ['class' => CustomFileCache::class]],
 | 
			
		||||
      [],
 | 
			
		||||
      CustomFileCache::class,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    // Test default configuration plus specific per collection setting.
 | 
			
		||||
    $data['default-plus-collection-setting'] = [
 | 
			
		||||
      [
 | 
			
		||||
        'default' => ['class' => '\stdClass'],
 | 
			
		||||
        'test_foo_settings' => ['class' => CustomFileCache::class],
 | 
			
		||||
      ],
 | 
			
		||||
      [],
 | 
			
		||||
      CustomFileCache::class,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    // Test default configuration plus class specific override.
 | 
			
		||||
    $data['default-plus-class-override'] = [
 | 
			
		||||
      ['default' => ['class' => '\stdClass']],
 | 
			
		||||
      ['class' => CustomFileCache::class],
 | 
			
		||||
      CustomFileCache::class,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    // Test default configuration plus class specific override plus specific
 | 
			
		||||
    // per collection setting.
 | 
			
		||||
    $data['default-plus-class-plus-collection-setting'] = [
 | 
			
		||||
      [
 | 
			
		||||
        'default' => ['class' => '\stdClass'],
 | 
			
		||||
        'test_foo_settings' => ['class' => CustomFileCache::class],
 | 
			
		||||
      ],
 | 
			
		||||
      ['class' => '\stdClass'],
 | 
			
		||||
      CustomFileCache::class,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    return $data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getConfiguration
 | 
			
		||||
   * @covers ::setConfiguration
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetSetConfiguration(): void {
 | 
			
		||||
    $configuration = FileCacheFactory::getConfiguration();
 | 
			
		||||
    $configuration['test_foo_bar'] = ['bar' => 'llama'];
 | 
			
		||||
    FileCacheFactory::setConfiguration($configuration);
 | 
			
		||||
    $configuration = FileCacheFactory::getConfiguration();
 | 
			
		||||
    $this->assertEquals(['bar' => 'llama'], $configuration['test_foo_bar']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getPrefix
 | 
			
		||||
   * @covers ::setPrefix
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetSetPrefix(): void {
 | 
			
		||||
    // Random generator.
 | 
			
		||||
    $random = new Random();
 | 
			
		||||
 | 
			
		||||
    $prefix = $random->name(8, TRUE);
 | 
			
		||||
    FileCacheFactory::setPrefix($prefix);
 | 
			
		||||
    $this->assertEquals($prefix, FileCacheFactory::getPrefix());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class to help test the file cache class.
 | 
			
		||||
 */
 | 
			
		||||
class CustomFileCache extends FileCache {}
 | 
			
		||||
@ -0,0 +1,144 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\FileCache;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\FileCache\FileCache;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\FileCache\FileCache
 | 
			
		||||
 * @group FileCache
 | 
			
		||||
 */
 | 
			
		||||
class FileCacheTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * FileCache object used for the tests.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Drupal\Component\FileCache\FileCacheInterface
 | 
			
		||||
   */
 | 
			
		||||
  protected $fileCache;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Static FileCache object used for verification of tests.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Drupal\Component\FileCache\FileCacheBackendInterface
 | 
			
		||||
   */
 | 
			
		||||
  protected $staticFileCache;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    $this->fileCache = new FileCache('prefix', 'test', '\Drupal\Tests\Component\FileCache\StaticFileCacheBackend', ['bin' => 'llama']);
 | 
			
		||||
    $this->staticFileCache = new StaticFileCacheBackend(['bin' => 'llama']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::get
 | 
			
		||||
   * @covers ::__construct
 | 
			
		||||
   */
 | 
			
		||||
  public function testGet(): void {
 | 
			
		||||
    // Test a cache miss.
 | 
			
		||||
    $result = $this->fileCache->get(__DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'no-llama-42.yml');
 | 
			
		||||
    $this->assertNull($result);
 | 
			
		||||
 | 
			
		||||
    // Test a cache hit.
 | 
			
		||||
    $filename = __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'llama-42.txt';
 | 
			
		||||
    $realpath = realpath($filename);
 | 
			
		||||
    $cid = 'prefix:test:' . $realpath;
 | 
			
		||||
    $data = [
 | 
			
		||||
      'mtime' => filemtime($realpath),
 | 
			
		||||
      'filepath' => $realpath,
 | 
			
		||||
      'data' => 42,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    $this->staticFileCache->store($cid, $data);
 | 
			
		||||
 | 
			
		||||
    $result = $this->fileCache->get($filename);
 | 
			
		||||
    $this->assertEquals(42, $result);
 | 
			
		||||
 | 
			
		||||
    // Cleanup static caches.
 | 
			
		||||
    $this->fileCache->delete($filename);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getMultiple
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetMultiple(): void {
 | 
			
		||||
    // Test a cache miss.
 | 
			
		||||
    $result = $this->fileCache->getMultiple([__DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'no-llama-42.yml']);
 | 
			
		||||
    $this->assertEmpty($result);
 | 
			
		||||
 | 
			
		||||
    // Test a cache hit.
 | 
			
		||||
    $filename = __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'llama-42.txt';
 | 
			
		||||
    $realpath = realpath($filename);
 | 
			
		||||
    $cid = 'prefix:test:' . $realpath;
 | 
			
		||||
    $data = [
 | 
			
		||||
      'mtime' => filemtime($realpath),
 | 
			
		||||
      'filepath' => $realpath,
 | 
			
		||||
      'data' => 42,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    $this->staticFileCache->store($cid, $data);
 | 
			
		||||
 | 
			
		||||
    $result = $this->fileCache->getMultiple([$filename]);
 | 
			
		||||
    $this->assertEquals([$filename => 42], $result);
 | 
			
		||||
 | 
			
		||||
    // Test a static cache hit.
 | 
			
		||||
    $file2 = __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'llama-23.txt';
 | 
			
		||||
    $this->fileCache->set($file2, 23);
 | 
			
		||||
 | 
			
		||||
    $result = $this->fileCache->getMultiple([$filename, $file2]);
 | 
			
		||||
    $this->assertEquals([$filename => 42, $file2 => 23], $result);
 | 
			
		||||
 | 
			
		||||
    // Cleanup static caches.
 | 
			
		||||
    $this->fileCache->delete($filename);
 | 
			
		||||
    $this->fileCache->delete($file2);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::set
 | 
			
		||||
   */
 | 
			
		||||
  public function testSet(): void {
 | 
			
		||||
    $filename = __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'llama-23.txt';
 | 
			
		||||
    $realpath = realpath($filename);
 | 
			
		||||
    $cid = 'prefix:test:' . $realpath;
 | 
			
		||||
    $data = [
 | 
			
		||||
      'mtime' => filemtime($realpath),
 | 
			
		||||
      'filepath' => $realpath,
 | 
			
		||||
      'data' => 23,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    $this->fileCache->set($filename, 23);
 | 
			
		||||
    $result = $this->staticFileCache->fetch([$cid]);
 | 
			
		||||
    $this->assertEquals([$cid => $data], $result);
 | 
			
		||||
 | 
			
		||||
    // Cleanup static caches.
 | 
			
		||||
    $this->fileCache->delete($filename);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::delete
 | 
			
		||||
   */
 | 
			
		||||
  public function testDelete(): void {
 | 
			
		||||
    $filename = __DIR__ . DIRECTORY_SEPARATOR . 'Fixtures' . DIRECTORY_SEPARATOR . 'llama-23.txt';
 | 
			
		||||
    $realpath = realpath($filename);
 | 
			
		||||
    $cid = 'prefix:test:' . $realpath;
 | 
			
		||||
 | 
			
		||||
    $this->fileCache->set($filename, 23);
 | 
			
		||||
 | 
			
		||||
    // Ensure data is removed after deletion.
 | 
			
		||||
    $this->fileCache->delete($filename);
 | 
			
		||||
 | 
			
		||||
    $result = $this->staticFileCache->fetch([$cid]);
 | 
			
		||||
    $this->assertEquals([], $result);
 | 
			
		||||
 | 
			
		||||
    $result = $this->fileCache->get($filename);
 | 
			
		||||
    $this->assertNull($result);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1 @@
 | 
			
		||||
23
 | 
			
		||||
@ -0,0 +1 @@
 | 
			
		||||
42
 | 
			
		||||
@ -0,0 +1,73 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\FileCache;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\FileCache\FileCacheBackendInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Allows to cache data based on file modification dates in a static cache.
 | 
			
		||||
 */
 | 
			
		||||
class StaticFileCacheBackend implements FileCacheBackendInterface {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Internal static cache.
 | 
			
		||||
   *
 | 
			
		||||
   * @var array
 | 
			
		||||
   */
 | 
			
		||||
  protected static $cache = [];
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Bin used for storing the data in the static cache.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   */
 | 
			
		||||
  protected $bin;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Constructs a PHP Storage FileCache backend.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $configuration
 | 
			
		||||
   *   (optional) Configuration used to configure this object.
 | 
			
		||||
   */
 | 
			
		||||
  public function __construct($configuration) {
 | 
			
		||||
    $this->bin = $configuration['bin'] ?? 'file_cache';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function fetch(array $cids) {
 | 
			
		||||
    $result = [];
 | 
			
		||||
    foreach ($cids as $cid) {
 | 
			
		||||
      if (isset(static::$cache[$this->bin][$cid])) {
 | 
			
		||||
        $result[$cid] = static::$cache[$this->bin][$cid];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return $result;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function store($cid, $data) {
 | 
			
		||||
    static::$cache[$this->bin][$cid] = $data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function delete($cid) {
 | 
			
		||||
    unset(static::$cache[$this->bin][$cid]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Allows tests to reset the static cache to avoid side effects.
 | 
			
		||||
   */
 | 
			
		||||
  public static function reset() {
 | 
			
		||||
    static::$cache = [];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,66 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\FileSecurity;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\FileSecurity\FileSecurity;
 | 
			
		||||
use org\bovigo\vfs\vfsStream;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests the file security component.
 | 
			
		||||
 *
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\FileSecurity\FileSecurity
 | 
			
		||||
 * @group FileSecurity
 | 
			
		||||
 */
 | 
			
		||||
class FileSecurityTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::writeHtaccess
 | 
			
		||||
   */
 | 
			
		||||
  public function testWriteHtaccessPrivate(): void {
 | 
			
		||||
    vfsStream::setup('root');
 | 
			
		||||
    FileSecurity::writeHtaccess(vfsStream::url('root'));
 | 
			
		||||
    $htaccess_file = vfsStream::url('root') . '/.htaccess';
 | 
			
		||||
    $this->assertFileExists($htaccess_file);
 | 
			
		||||
    $this->assertEquals('0444', substr(sprintf('%o', fileperms($htaccess_file)), -4));
 | 
			
		||||
    $htaccess_contents = file_get_contents($htaccess_file);
 | 
			
		||||
    $this->assertStringContainsString("Require all denied", $htaccess_contents);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::writeHtaccess
 | 
			
		||||
   */
 | 
			
		||||
  public function testWriteHtaccessPublic(): void {
 | 
			
		||||
    vfsStream::setup('root');
 | 
			
		||||
    $this->assertTrue(FileSecurity::writeHtaccess(vfsStream::url('root'), FALSE));
 | 
			
		||||
    $htaccess_file = vfsStream::url('root') . '/.htaccess';
 | 
			
		||||
    $this->assertFileExists($htaccess_file);
 | 
			
		||||
    $this->assertEquals('0444', substr(sprintf('%o', fileperms($htaccess_file)), -4));
 | 
			
		||||
    $htaccess_contents = file_get_contents($htaccess_file);
 | 
			
		||||
    $this->assertStringNotContainsString("Require all denied", $htaccess_contents);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::writeHtaccess
 | 
			
		||||
   */
 | 
			
		||||
  public function testWriteHtaccessForceOverwrite(): void {
 | 
			
		||||
    vfsStream::setup('root');
 | 
			
		||||
    $htaccess_file = vfsStream::url('root') . '/.htaccess';
 | 
			
		||||
    file_put_contents($htaccess_file, "foo");
 | 
			
		||||
    $this->assertTrue(FileSecurity::writeHtaccess(vfsStream::url('root'), TRUE, TRUE));
 | 
			
		||||
    $htaccess_contents = file_get_contents($htaccess_file);
 | 
			
		||||
    $this->assertStringContainsString("Require all denied", $htaccess_contents);
 | 
			
		||||
    $this->assertStringNotContainsString("foo", $htaccess_contents);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::writeHtaccess
 | 
			
		||||
   */
 | 
			
		||||
  public function testWriteHtaccessFailure(): void {
 | 
			
		||||
    vfsStream::setup('root');
 | 
			
		||||
    $this->assertFalse(FileSecurity::writeHtaccess(vfsStream::url('root') . '/foo'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,111 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\FileSystem;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\FileSystem\RegexDirectoryIterator;
 | 
			
		||||
use org\bovigo\vfs\vfsStream;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\FileSystem\RegexDirectoryIterator
 | 
			
		||||
 * @group FileSystem
 | 
			
		||||
 */
 | 
			
		||||
class RegexDirectoryIteratorTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::accept
 | 
			
		||||
   * @dataProvider providerTestRegexDirectoryIterator
 | 
			
		||||
   */
 | 
			
		||||
  public function testRegexDirectoryIterator(array $directory, $regex, array $expected): void {
 | 
			
		||||
    vfsStream::setup('root', NULL, $directory);
 | 
			
		||||
    $iterator = new RegexDirectoryIterator(vfsStream::url('root'), $regex);
 | 
			
		||||
 | 
			
		||||
    // Create an array of filenames to assert against.
 | 
			
		||||
    $file_list = array_map(function (\SplFileInfo $file) {
 | 
			
		||||
      return $file->getFilename();
 | 
			
		||||
    }, array_values(iterator_to_array($iterator)));
 | 
			
		||||
 | 
			
		||||
    $this->assertSame($expected, $file_list);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Provider for self::testRegexDirectoryIterator().
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestRegexDirectoryIterator() {
 | 
			
		||||
    return [
 | 
			
		||||
      [
 | 
			
		||||
        [
 | 
			
		||||
          '1.yml' => '',
 | 
			
		||||
        ],
 | 
			
		||||
        '/\.yml$/',
 | 
			
		||||
        [
 | 
			
		||||
          '1.yml',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        [
 | 
			
		||||
          '1.yml' => '',
 | 
			
		||||
          '2.yml' => '',
 | 
			
		||||
          '3.txt' => '',
 | 
			
		||||
        ],
 | 
			
		||||
        '/\.yml$/',
 | 
			
		||||
        [
 | 
			
		||||
          '1.yml',
 | 
			
		||||
          '2.yml',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        [
 | 
			
		||||
          '1.yml' => '',
 | 
			
		||||
          '2.yml' => '',
 | 
			
		||||
          '3.txt' => '',
 | 
			
		||||
        ],
 | 
			
		||||
        '/\.txt/',
 | 
			
		||||
        [
 | 
			
		||||
          '3.txt',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        [
 | 
			
		||||
          '1.yml' => '',
 | 
			
		||||
          // Ensure we don't recurse in directories even if that match the
 | 
			
		||||
          // regex.
 | 
			
		||||
          '2.yml' => [
 | 
			
		||||
            '3.yml' => '',
 | 
			
		||||
            '4.yml' => '',
 | 
			
		||||
          ],
 | 
			
		||||
          '3.txt' => '',
 | 
			
		||||
        ],
 | 
			
		||||
        '/\.yml$/',
 | 
			
		||||
        [
 | 
			
		||||
          '1.yml',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        [
 | 
			
		||||
          '1.yml' => '',
 | 
			
		||||
          '2.yml' => '',
 | 
			
		||||
          '3.txt' => '',
 | 
			
		||||
        ],
 | 
			
		||||
        '/^\d/',
 | 
			
		||||
        [
 | 
			
		||||
          '1.yml',
 | 
			
		||||
          '2.yml',
 | 
			
		||||
          '3.txt',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        [
 | 
			
		||||
          '1.yml' => '',
 | 
			
		||||
          '2.yml' => '',
 | 
			
		||||
          '3.txt' => '',
 | 
			
		||||
        ],
 | 
			
		||||
        '/^\D/',
 | 
			
		||||
        [],
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,174 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\FrontMatter;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\FrontMatter\Exception\FrontMatterParseException;
 | 
			
		||||
use Drupal\Component\FrontMatter\FrontMatter;
 | 
			
		||||
use Drupal\Component\Serialization\Yaml;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests front matter parsing helper methods.
 | 
			
		||||
 *
 | 
			
		||||
 * @group FrontMatter
 | 
			
		||||
 *
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\FrontMatter\FrontMatter
 | 
			
		||||
 */
 | 
			
		||||
class FrontMatterTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * A basic source string.
 | 
			
		||||
   */
 | 
			
		||||
  const SOURCE = '<div>Hello world</div>';
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a front matter source string.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array|null $yaml
 | 
			
		||||
   *   The YAML array to prepend as a front matter block.
 | 
			
		||||
   * @param string $content
 | 
			
		||||
   *   The source contents.
 | 
			
		||||
   *
 | 
			
		||||
   * @return string
 | 
			
		||||
   *   The new source.
 | 
			
		||||
   */
 | 
			
		||||
  public static function createFrontMatterSource(?array $yaml, string $content = self::SOURCE): string {
 | 
			
		||||
    // Encode YAML and wrap in a front matter block.
 | 
			
		||||
    $frontMatter = '';
 | 
			
		||||
    if (is_array($yaml)) {
 | 
			
		||||
      $yaml = $yaml ? trim(Yaml::encode($yaml)) . "\n" : '';
 | 
			
		||||
      $frontMatter = FrontMatter::SEPARATOR . "\n$yaml" . FrontMatter::SEPARATOR . "\n";
 | 
			
		||||
    }
 | 
			
		||||
    return $frontMatter . $content;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests when a passed serializer doesn't implement the proper interface.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::__construct
 | 
			
		||||
   * @covers ::create
 | 
			
		||||
   */
 | 
			
		||||
  public function testFrontMatterSerializerException(): void {
 | 
			
		||||
    $this->expectException(\AssertionError::class);
 | 
			
		||||
    $this->expectExceptionMessage('The $serializer parameter must reference a class that implements Drupal\Component\Serialization\SerializationInterface.');
 | 
			
		||||
    FrontMatter::create('', '');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests broken front matter.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::__construct
 | 
			
		||||
   * @covers ::create
 | 
			
		||||
   * @covers ::parse
 | 
			
		||||
   * @covers \Drupal\Component\FrontMatter\Exception\FrontMatterParseException
 | 
			
		||||
   */
 | 
			
		||||
  public function testFrontMatterBroken(): void {
 | 
			
		||||
    $this->expectException(FrontMatterParseException::class);
 | 
			
		||||
    $this->expectExceptionMessage('An error occurred when attempting to parse front matter data on line 4');
 | 
			
		||||
    $source = "---\ncollection:\n-  key: foo\n  foo: bar\n---\n";
 | 
			
		||||
    FrontMatter::create($source)->getData();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests the parsed data from front matter.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array|null $yaml
 | 
			
		||||
   *   The YAML used as front matter data to prepend the source.
 | 
			
		||||
   * @param int $line
 | 
			
		||||
   *   The expected line number where the source code starts.
 | 
			
		||||
   * @param string $content
 | 
			
		||||
   *   The content to use for testing purposes.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::__construct
 | 
			
		||||
   * @covers ::getContent
 | 
			
		||||
   * @covers ::getData
 | 
			
		||||
   * @covers ::getLine
 | 
			
		||||
   * @covers ::create
 | 
			
		||||
   * @covers ::parse
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerFrontMatterData
 | 
			
		||||
   */
 | 
			
		||||
  public function testFrontMatterData($yaml, $line, $content = self::SOURCE): void {
 | 
			
		||||
    $source = static::createFrontMatterSource($yaml, $content);
 | 
			
		||||
    $frontMatter = FrontMatter::create($source);
 | 
			
		||||
    $this->assertEquals($content, $frontMatter->getContent());
 | 
			
		||||
    $this->assertEquals($yaml ?? [], $frontMatter->getData());
 | 
			
		||||
    $this->assertEquals($line, $frontMatter->getLine());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Provides the front matter data to test.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   Array of front matter data.
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerFrontMatterData() {
 | 
			
		||||
    $data['none'] = [
 | 
			
		||||
      'yaml' => NULL,
 | 
			
		||||
      'line' => 1,
 | 
			
		||||
    ];
 | 
			
		||||
    $data['scalar'] = [
 | 
			
		||||
      'yaml' => [
 | 
			
		||||
        'string' => 'value',
 | 
			
		||||
        'number' => 42,
 | 
			
		||||
        'bool' => TRUE,
 | 
			
		||||
        'null' => NULL,
 | 
			
		||||
      ],
 | 
			
		||||
      'line' => 7,
 | 
			
		||||
    ];
 | 
			
		||||
    $data['indexed_arrays'] = [
 | 
			
		||||
      'yaml' => [
 | 
			
		||||
        'brackets' => [1, 2, 3],
 | 
			
		||||
        'items' => [
 | 
			
		||||
          'item1',
 | 
			
		||||
          'item2',
 | 
			
		||||
          'item3',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      'line' => 11,
 | 
			
		||||
    ];
 | 
			
		||||
    $data['associative_arrays'] = [
 | 
			
		||||
      'yaml' => [
 | 
			
		||||
        'brackets' => [
 | 
			
		||||
          'a' => 1,
 | 
			
		||||
          'b' => 2,
 | 
			
		||||
          'c' => 3,
 | 
			
		||||
        ],
 | 
			
		||||
        'items' => [
 | 
			
		||||
          'a' => 'item1',
 | 
			
		||||
          'b' => 'item2',
 | 
			
		||||
          'c' => 'item3',
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      'line' => 11,
 | 
			
		||||
    ];
 | 
			
		||||
    $data['empty_data'] = [
 | 
			
		||||
      'yaml' => [],
 | 
			
		||||
      'line' => 3,
 | 
			
		||||
    ];
 | 
			
		||||
    $data['empty_content'] = [
 | 
			
		||||
      'yaml' => ['key' => 'value'],
 | 
			
		||||
      'line' => 4,
 | 
			
		||||
      'content' => '',
 | 
			
		||||
    ];
 | 
			
		||||
    $data['empty_data_and_content'] = [
 | 
			
		||||
      'yaml' => [],
 | 
			
		||||
      'line' => 3,
 | 
			
		||||
      'content' => '',
 | 
			
		||||
    ];
 | 
			
		||||
    $data['empty_string'] = [
 | 
			
		||||
      'yaml' => NULL,
 | 
			
		||||
      'line' => 1,
 | 
			
		||||
      'content' => '',
 | 
			
		||||
    ];
 | 
			
		||||
    $data['multiple_separators'] = [
 | 
			
		||||
      'yaml' => ['key' => '---'],
 | 
			
		||||
      'line' => 4,
 | 
			
		||||
      'content' => "Something\n---\nSomething more",
 | 
			
		||||
    ];
 | 
			
		||||
    return $data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										372
									
								
								web/core/tests/Drupal/Tests/Component/Gettext/PoHeaderTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										372
									
								
								web/core/tests/Drupal/Tests/Component/Gettext/PoHeaderTest.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,372 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Gettext;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Gettext\PoHeader;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Unit tests for the Gettext PO file header handling features.
 | 
			
		||||
 *
 | 
			
		||||
 * @see Drupal\Component\Gettext\PoHeader.
 | 
			
		||||
 *
 | 
			
		||||
 * @group Gettext
 | 
			
		||||
 */
 | 
			
		||||
class PoHeaderTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that plural expressions are evaluated correctly.
 | 
			
		||||
   *
 | 
			
		||||
   * Validate that the given plural expressions is evaluated with the correct
 | 
			
		||||
   * plural formula.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $plural
 | 
			
		||||
   *   The plural expression.
 | 
			
		||||
   * @param array $expected
 | 
			
		||||
   *   Array of expected plural positions keyed by plural value.
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider providerTestPluralsFormula
 | 
			
		||||
   */
 | 
			
		||||
  public function testPluralsFormula($plural, $expected): void {
 | 
			
		||||
    $p = new PoHeader();
 | 
			
		||||
    [, $new_plural] = $p->parsePluralForms($plural);
 | 
			
		||||
    foreach ($expected as $number => $plural_form) {
 | 
			
		||||
      $result = $new_plural[$number] ?? $new_plural['default'];
 | 
			
		||||
      $this->assertEquals($result, $plural_form, 'Difference found at ' . $number . ': ' . $plural_form . ' versus ' . $result);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider for testPluralsFormula.
 | 
			
		||||
   *
 | 
			
		||||
   * Gets pairs of plural expressions and expected plural positions keyed by
 | 
			
		||||
   * plural value.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   Pairs of plural expressions and expected plural positions keyed by plural
 | 
			
		||||
   *   value.
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerTestPluralsFormula() {
 | 
			
		||||
    return [
 | 
			
		||||
      [
 | 
			
		||||
        'nplurals=1; plural=0;',
 | 
			
		||||
        ['default' => 0],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'nplurals=2; plural=(n > 1);',
 | 
			
		||||
        [0 => 0, 1 => 0, 'default' => 1],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'nplurals=2; plural=(n!=1);',
 | 
			
		||||
        [1 => 0, 'default' => 1],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'nplurals=2; plural=(((n==1)||((n%10)==1))?(0):1);',
 | 
			
		||||
        [
 | 
			
		||||
          1 => 0,
 | 
			
		||||
          11 => 0,
 | 
			
		||||
          21 => 0,
 | 
			
		||||
          31 => 0,
 | 
			
		||||
          41 => 0,
 | 
			
		||||
          51 => 0,
 | 
			
		||||
          61 => 0,
 | 
			
		||||
          71 => 0,
 | 
			
		||||
          81 => 0,
 | 
			
		||||
          91 => 0,
 | 
			
		||||
          101 => 0,
 | 
			
		||||
          111 => 0,
 | 
			
		||||
          121 => 0,
 | 
			
		||||
          131 => 0,
 | 
			
		||||
          141 => 0,
 | 
			
		||||
          151 => 0,
 | 
			
		||||
          161 => 0,
 | 
			
		||||
          171 => 0,
 | 
			
		||||
          181 => 0,
 | 
			
		||||
          191 => 0,
 | 
			
		||||
          'default' => 1,
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'nplurals=3; plural=((((n%10)==1)&&((n%100)!=11))?(0):(((((n%10)>=2)&&((n%10)<=4))&&(((n%100)<10)||((n%100)>=20)))?(1):2));',
 | 
			
		||||
        [
 | 
			
		||||
          1 => 0,
 | 
			
		||||
          2 => 1,
 | 
			
		||||
          3 => 1,
 | 
			
		||||
          4 => 1,
 | 
			
		||||
          21 => 0,
 | 
			
		||||
          22 => 1,
 | 
			
		||||
          23 => 1,
 | 
			
		||||
          24 => 1,
 | 
			
		||||
          31 => 0,
 | 
			
		||||
          32 => 1,
 | 
			
		||||
          33 => 1,
 | 
			
		||||
          34 => 1,
 | 
			
		||||
          41 => 0,
 | 
			
		||||
          42 => 1,
 | 
			
		||||
          43 => 1,
 | 
			
		||||
          44 => 1,
 | 
			
		||||
          51 => 0,
 | 
			
		||||
          52 => 1,
 | 
			
		||||
          53 => 1,
 | 
			
		||||
          54 => 1,
 | 
			
		||||
          61 => 0,
 | 
			
		||||
          62 => 1,
 | 
			
		||||
          63 => 1,
 | 
			
		||||
          64 => 1,
 | 
			
		||||
          71 => 0,
 | 
			
		||||
          72 => 1,
 | 
			
		||||
          73 => 1,
 | 
			
		||||
          74 => 1,
 | 
			
		||||
          81 => 0,
 | 
			
		||||
          82 => 1,
 | 
			
		||||
          83 => 1,
 | 
			
		||||
          84 => 1,
 | 
			
		||||
          91 => 0,
 | 
			
		||||
          92 => 1,
 | 
			
		||||
          93 => 1,
 | 
			
		||||
          94 => 1,
 | 
			
		||||
          101 => 0,
 | 
			
		||||
          102 => 1,
 | 
			
		||||
          103 => 1,
 | 
			
		||||
          104 => 1,
 | 
			
		||||
          121 => 0,
 | 
			
		||||
          122 => 1,
 | 
			
		||||
          123 => 1,
 | 
			
		||||
          124 => 1,
 | 
			
		||||
          131 => 0,
 | 
			
		||||
          132 => 1,
 | 
			
		||||
          133 => 1,
 | 
			
		||||
          134 => 1,
 | 
			
		||||
          141 => 0,
 | 
			
		||||
          142 => 1,
 | 
			
		||||
          143 => 1,
 | 
			
		||||
          144 => 1,
 | 
			
		||||
          151 => 0,
 | 
			
		||||
          152 => 1,
 | 
			
		||||
          153 => 1,
 | 
			
		||||
          154 => 1,
 | 
			
		||||
          161 => 0,
 | 
			
		||||
          162 => 1,
 | 
			
		||||
          163 => 1,
 | 
			
		||||
          164 => 1,
 | 
			
		||||
          171 => 0,
 | 
			
		||||
          172 => 1,
 | 
			
		||||
          173 => 1,
 | 
			
		||||
          174 => 1,
 | 
			
		||||
          181 => 0,
 | 
			
		||||
          182 => 1,
 | 
			
		||||
          183 => 1,
 | 
			
		||||
          184 => 1,
 | 
			
		||||
          191 => 0,
 | 
			
		||||
          192 => 1,
 | 
			
		||||
          193 => 1,
 | 
			
		||||
          194 => 1,
 | 
			
		||||
          'default' => 2,
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'nplurals=3; plural=((n==1)?(0):(((n>=2)&&(n<=4))?(1):2));',
 | 
			
		||||
        [
 | 
			
		||||
          1 => 0,
 | 
			
		||||
          2 => 1,
 | 
			
		||||
          3 => 1,
 | 
			
		||||
          4 => 1,
 | 
			
		||||
          'default' => 2,
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'nplurals=3; plural=((n==1)?(0):(((n==0)||(((n%100)>0)&&((n%100)<20)))?(1):2));',
 | 
			
		||||
        [
 | 
			
		||||
          0 => 1,
 | 
			
		||||
          1 => 0,
 | 
			
		||||
          2 => 1,
 | 
			
		||||
          3 => 1,
 | 
			
		||||
          4 => 1,
 | 
			
		||||
          5 => 1,
 | 
			
		||||
          6 => 1,
 | 
			
		||||
          7 => 1,
 | 
			
		||||
          8 => 1,
 | 
			
		||||
          9 => 1,
 | 
			
		||||
          10 => 1,
 | 
			
		||||
          11 => 1,
 | 
			
		||||
          12 => 1,
 | 
			
		||||
          13 => 1,
 | 
			
		||||
          14 => 1,
 | 
			
		||||
          15 => 1,
 | 
			
		||||
          16 => 1,
 | 
			
		||||
          17 => 1,
 | 
			
		||||
          18 => 1,
 | 
			
		||||
          19 => 1,
 | 
			
		||||
          101 => 1,
 | 
			
		||||
          102 => 1,
 | 
			
		||||
          103 => 1,
 | 
			
		||||
          104 => 1,
 | 
			
		||||
          105 => 1,
 | 
			
		||||
          106 => 1,
 | 
			
		||||
          107 => 1,
 | 
			
		||||
          108 => 1,
 | 
			
		||||
          109 => 1,
 | 
			
		||||
          110 => 1,
 | 
			
		||||
          111 => 1,
 | 
			
		||||
          112 => 1,
 | 
			
		||||
          113 => 1,
 | 
			
		||||
          114 => 1,
 | 
			
		||||
          115 => 1,
 | 
			
		||||
          116 => 1,
 | 
			
		||||
          117 => 1,
 | 
			
		||||
          118 => 1,
 | 
			
		||||
          119 => 1,
 | 
			
		||||
          'default' => 2,
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'nplurals=3; plural=((n==1)?(0):(((((n%10)>=2)&&((n%10)<=4))&&(((n%100)<10)||((n%100)>=20)))?(1):2));',
 | 
			
		||||
        [
 | 
			
		||||
          1 => 0,
 | 
			
		||||
          2 => 1,
 | 
			
		||||
          3 => 1,
 | 
			
		||||
          4 => 1,
 | 
			
		||||
          22 => 1,
 | 
			
		||||
          23 => 1,
 | 
			
		||||
          24 => 1,
 | 
			
		||||
          32 => 1,
 | 
			
		||||
          33 => 1,
 | 
			
		||||
          34 => 1,
 | 
			
		||||
          42 => 1,
 | 
			
		||||
          43 => 1,
 | 
			
		||||
          44 => 1,
 | 
			
		||||
          52 => 1,
 | 
			
		||||
          53 => 1,
 | 
			
		||||
          54 => 1,
 | 
			
		||||
          62 => 1,
 | 
			
		||||
          63 => 1,
 | 
			
		||||
          64 => 1,
 | 
			
		||||
          72 => 1,
 | 
			
		||||
          73 => 1,
 | 
			
		||||
          74 => 1,
 | 
			
		||||
          82 => 1,
 | 
			
		||||
          83 => 1,
 | 
			
		||||
          84 => 1,
 | 
			
		||||
          92 => 1,
 | 
			
		||||
          93 => 1,
 | 
			
		||||
          94 => 1,
 | 
			
		||||
          102 => 1,
 | 
			
		||||
          103 => 1,
 | 
			
		||||
          104 => 1,
 | 
			
		||||
          122 => 1,
 | 
			
		||||
          123 => 1,
 | 
			
		||||
          124 => 1,
 | 
			
		||||
          132 => 1,
 | 
			
		||||
          133 => 1,
 | 
			
		||||
          134 => 1,
 | 
			
		||||
          142 => 1,
 | 
			
		||||
          143 => 1,
 | 
			
		||||
          144 => 1,
 | 
			
		||||
          152 => 1,
 | 
			
		||||
          153 => 1,
 | 
			
		||||
          154 => 1,
 | 
			
		||||
          162 => 1,
 | 
			
		||||
          163 => 1,
 | 
			
		||||
          164 => 1,
 | 
			
		||||
          172 => 1,
 | 
			
		||||
          173 => 1,
 | 
			
		||||
          174 => 1,
 | 
			
		||||
          182 => 1,
 | 
			
		||||
          183 => 1,
 | 
			
		||||
          184 => 1,
 | 
			
		||||
          192 => 1,
 | 
			
		||||
          193 => 1,
 | 
			
		||||
          194 => 1,
 | 
			
		||||
          'default' => 2,
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'nplurals=4; plural=(((n==1)||(n==11))?(0):(((n==2)||(n==12))?(1):(((n>2)&&(n<20))?(2):3)));',
 | 
			
		||||
        [
 | 
			
		||||
          1 => 0,
 | 
			
		||||
          2 => 1,
 | 
			
		||||
          3 => 2,
 | 
			
		||||
          4 => 2,
 | 
			
		||||
          5 => 2,
 | 
			
		||||
          6 => 2,
 | 
			
		||||
          7 => 2,
 | 
			
		||||
          8 => 2,
 | 
			
		||||
          9 => 2,
 | 
			
		||||
          10 => 2,
 | 
			
		||||
          11 => 0,
 | 
			
		||||
          12 => 1,
 | 
			
		||||
          13 => 2,
 | 
			
		||||
          14 => 2,
 | 
			
		||||
          15 => 2,
 | 
			
		||||
          16 => 2,
 | 
			
		||||
          17 => 2,
 | 
			
		||||
          18 => 2,
 | 
			
		||||
          19 => 2,
 | 
			
		||||
          'default' => 3,
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'nplurals=4; plural=(((n%100)==1)?(0):(((n%100)==2)?(1):((((n%100)==3)||((n%100)==4))?(2):3)));',
 | 
			
		||||
        [
 | 
			
		||||
          1 => 0,
 | 
			
		||||
          2 => 1,
 | 
			
		||||
          3 => 2,
 | 
			
		||||
          4 => 2,
 | 
			
		||||
          101 => 0,
 | 
			
		||||
          102 => 1,
 | 
			
		||||
          103 => 2,
 | 
			
		||||
          104 => 2,
 | 
			
		||||
          'default' => 3,
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'nplurals=5; plural=((n==1)?(0):((n==2)?(1):((n<7)?(2):((n<11)?(3):4))));',
 | 
			
		||||
        [
 | 
			
		||||
          0 => 2,
 | 
			
		||||
          1 => 0,
 | 
			
		||||
          2 => 1,
 | 
			
		||||
          3 => 2,
 | 
			
		||||
          4 => 2,
 | 
			
		||||
          5 => 2,
 | 
			
		||||
          6 => 2,
 | 
			
		||||
          7 => 3,
 | 
			
		||||
          8 => 3,
 | 
			
		||||
          9 => 3,
 | 
			
		||||
          10 => 3,
 | 
			
		||||
          'default' => 4,
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
      [
 | 
			
		||||
        'nplurals=6; plural=((n==1)?(0):((n==0)?(1):((n==2)?(2):((((n%100)>=3)&&((n%100)<=10))?(3):((((n%100)>=11)&&((n%100)<=99))?(4):5)))));',
 | 
			
		||||
        [
 | 
			
		||||
          0 => 1,
 | 
			
		||||
          1 => 0,
 | 
			
		||||
          2 => 2,
 | 
			
		||||
          3 => 3,
 | 
			
		||||
          4 => 3,
 | 
			
		||||
          5 => 3,
 | 
			
		||||
          6 => 3,
 | 
			
		||||
          7 => 3,
 | 
			
		||||
          8 => 3,
 | 
			
		||||
          9 => 3,
 | 
			
		||||
          10 => 3,
 | 
			
		||||
          100 => 5,
 | 
			
		||||
          101 => 5,
 | 
			
		||||
          102 => 5,
 | 
			
		||||
          103 => 3,
 | 
			
		||||
          104 => 3,
 | 
			
		||||
          105 => 3,
 | 
			
		||||
          106 => 3,
 | 
			
		||||
          107 => 3,
 | 
			
		||||
          108 => 3,
 | 
			
		||||
          109 => 3,
 | 
			
		||||
          110 => 3,
 | 
			
		||||
          'default' => 4,
 | 
			
		||||
        ],
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,117 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Gettext;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Gettext\PoHeader;
 | 
			
		||||
use Drupal\Component\Gettext\PoItem;
 | 
			
		||||
use Drupal\Component\Gettext\PoStreamWriter;
 | 
			
		||||
use org\bovigo\vfs\vfsStream;
 | 
			
		||||
use org\bovigo\vfs\vfsStreamFile;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Prophecy\PhpUnit\ProphecyTrait;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Gettext\PoStreamWriter
 | 
			
		||||
 * @group Gettext
 | 
			
		||||
 */
 | 
			
		||||
class PoStreamWriterTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  use ProphecyTrait;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The PO writer object under test.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Drupal\Component\Gettext\PoStreamWriter
 | 
			
		||||
   */
 | 
			
		||||
  protected $poWriter;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The mock po file.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \org\bovigo\vfs\vfsStreamFile
 | 
			
		||||
   */
 | 
			
		||||
  protected $poFile;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    $poHeader = $this->prophesize(PoHeader::class);
 | 
			
		||||
    $poHeader->__toString()->willReturn('');
 | 
			
		||||
    $this->poWriter = new PoStreamWriter();
 | 
			
		||||
    $this->poWriter->setHeader($poHeader->reveal());
 | 
			
		||||
 | 
			
		||||
    $root = vfsStream::setup();
 | 
			
		||||
    $this->poFile = new vfsStreamFile('poWriter.po');
 | 
			
		||||
    $root->addChild($this->poFile);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getURI
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetUriException(): void {
 | 
			
		||||
    $this->expectException(\Exception::class);
 | 
			
		||||
    $this->expectExceptionMessage('No URI set.');
 | 
			
		||||
 | 
			
		||||
    $this->poWriter->getURI();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::writeItem
 | 
			
		||||
   * @dataProvider providerWriteData
 | 
			
		||||
   */
 | 
			
		||||
  public function testWriteItem($poContent, $expected, $long): void {
 | 
			
		||||
    if ($long) {
 | 
			
		||||
      $this->expectException(\Exception::class);
 | 
			
		||||
      $this->expectExceptionMessage('Unable to write data:');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Limit the file system quota to make the write fail on long strings.
 | 
			
		||||
    vfsStream::setQuota(10);
 | 
			
		||||
 | 
			
		||||
    $this->poWriter->setURI($this->poFile->url());
 | 
			
		||||
    $this->poWriter->open();
 | 
			
		||||
 | 
			
		||||
    $poItem = $this->prophesize(PoItem::class);
 | 
			
		||||
    $poItem->__toString()->willReturn($poContent);
 | 
			
		||||
 | 
			
		||||
    $this->poWriter->writeItem($poItem->reveal());
 | 
			
		||||
    $this->poWriter->close();
 | 
			
		||||
    $this->assertEquals(file_get_contents($this->poFile->url()), $expected);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   - Content to write.
 | 
			
		||||
   *   - Written content.
 | 
			
		||||
   *   - Content longer than 10 bytes.
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerWriteData() {
 | 
			
		||||
    // cSpell:disable
 | 
			
		||||
    return [
 | 
			
		||||
      ['', '', FALSE],
 | 
			
		||||
      ["\r\n", "\r\n", FALSE],
 | 
			
		||||
      ['write this if you can', 'write this', TRUE],
 | 
			
		||||
      ['éáíó>&', 'éáíó>&', FALSE],
 | 
			
		||||
      ['éáíó>&<', 'éáíó>&', TRUE],
 | 
			
		||||
      ['中文 890', '中文 890', FALSE],
 | 
			
		||||
      ['中文 89012', '中文 890', TRUE],
 | 
			
		||||
    ];
 | 
			
		||||
    // cSpell:enable
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::close
 | 
			
		||||
   */
 | 
			
		||||
  public function testCloseException(): void {
 | 
			
		||||
    $this->expectException(\Exception::class);
 | 
			
		||||
    $this->expectExceptionMessage('Cannot close stream that is not open.');
 | 
			
		||||
 | 
			
		||||
    $this->poWriter->close();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										204
									
								
								web/core/tests/Drupal/Tests/Component/Graph/GraphTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								web/core/tests/Drupal/Tests/Component/Graph/GraphTest.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,204 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Graph;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Graph\Graph;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Graph\Graph
 | 
			
		||||
 * @group Graph
 | 
			
		||||
 */
 | 
			
		||||
class GraphTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests depth-first-search features.
 | 
			
		||||
   */
 | 
			
		||||
  public function testDepthFirstSearch(): void {
 | 
			
		||||
    // The sample graph used is:
 | 
			
		||||
    // @code
 | 
			
		||||
    // 1 --> 2 --> 3     5 ---> 6
 | 
			
		||||
    //       |     ^     ^
 | 
			
		||||
    //       |     |     |
 | 
			
		||||
    //       |     |     |
 | 
			
		||||
    //       +---> 4 <-- 7      8 ---> 9
 | 
			
		||||
    // @endcode
 | 
			
		||||
    $graph = $this->normalizeGraph([
 | 
			
		||||
      1 => [2],
 | 
			
		||||
      2 => [3, 4],
 | 
			
		||||
      3 => [],
 | 
			
		||||
      4 => [3],
 | 
			
		||||
      5 => [6],
 | 
			
		||||
      7 => [4, 5],
 | 
			
		||||
      8 => [9],
 | 
			
		||||
      9 => [],
 | 
			
		||||
    ]);
 | 
			
		||||
    $graph_object = new Graph($graph);
 | 
			
		||||
    $graph = $graph_object->searchAndSort();
 | 
			
		||||
 | 
			
		||||
    $expected_paths = [
 | 
			
		||||
      1 => [2, 3, 4],
 | 
			
		||||
      2 => [3, 4],
 | 
			
		||||
      3 => [],
 | 
			
		||||
      4 => [3],
 | 
			
		||||
      5 => [6],
 | 
			
		||||
      7 => [4, 3, 5, 6],
 | 
			
		||||
      8 => [9],
 | 
			
		||||
      9 => [],
 | 
			
		||||
    ];
 | 
			
		||||
    $this->assertPaths($graph, $expected_paths);
 | 
			
		||||
 | 
			
		||||
    $expected_reverse_paths = [
 | 
			
		||||
      1 => [],
 | 
			
		||||
      2 => [1],
 | 
			
		||||
      3 => [2, 1, 4, 7],
 | 
			
		||||
      4 => [2, 1, 7],
 | 
			
		||||
      5 => [7],
 | 
			
		||||
      7 => [],
 | 
			
		||||
      8 => [],
 | 
			
		||||
      9 => [8],
 | 
			
		||||
    ];
 | 
			
		||||
    $this->assertReversePaths($graph, $expected_reverse_paths);
 | 
			
		||||
 | 
			
		||||
    // Assert that DFS didn't created "missing" vertexes automatically.
 | 
			
		||||
    $this->assertFalse(isset($graph[6]), 'Vertex 6 has not been created');
 | 
			
		||||
 | 
			
		||||
    $expected_components = [
 | 
			
		||||
      [1, 2, 3, 4, 5, 7],
 | 
			
		||||
      [8, 9],
 | 
			
		||||
    ];
 | 
			
		||||
    $this->assertComponents($graph, $expected_components);
 | 
			
		||||
 | 
			
		||||
    $expected_weights = [
 | 
			
		||||
      [1, 2, 3],
 | 
			
		||||
      [2, 4, 3],
 | 
			
		||||
      [7, 4, 3],
 | 
			
		||||
      [7, 5],
 | 
			
		||||
      [8, 9],
 | 
			
		||||
    ];
 | 
			
		||||
    $this->assertWeights($graph, $expected_weights);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Normalizes a graph.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $graph
 | 
			
		||||
   *   A graph array processed by \Drupal\Component\Graph\Graph::searchAndSort()
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   The normalized version of a graph.
 | 
			
		||||
   */
 | 
			
		||||
  protected function normalizeGraph($graph): array {
 | 
			
		||||
    $normalized_graph = [];
 | 
			
		||||
    foreach ($graph as $vertex => $edges) {
 | 
			
		||||
      // Create vertex even if it hasn't any edges.
 | 
			
		||||
      $normalized_graph[$vertex] = [];
 | 
			
		||||
      foreach ($edges as $edge) {
 | 
			
		||||
        $normalized_graph[$vertex]['edges'][$edge] = TRUE;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return $normalized_graph;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Verify expected paths in a graph.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $graph
 | 
			
		||||
   *   A graph array processed by \Drupal\Component\Graph\Graph::searchAndSort()
 | 
			
		||||
   * @param array $expected_paths
 | 
			
		||||
   *   An associative array containing vertices with their expected paths.
 | 
			
		||||
   *
 | 
			
		||||
   * @internal
 | 
			
		||||
   */
 | 
			
		||||
  protected function assertPaths(array $graph, array $expected_paths): void {
 | 
			
		||||
    foreach ($expected_paths as $vertex => $paths) {
 | 
			
		||||
      // Build an array with keys = $paths and values = TRUE.
 | 
			
		||||
      $expected = array_fill_keys($paths, TRUE);
 | 
			
		||||
      $result = $graph[$vertex]['paths'] ?? [];
 | 
			
		||||
      $this->assertEquals($expected, $result, sprintf('Expected paths for vertex %s: %s, got %s', $vertex, $this->displayArray($expected, TRUE), $this->displayArray($result, TRUE)));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Verify expected reverse paths in a graph.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $graph
 | 
			
		||||
   *   A graph array processed by \Drupal\Component\Graph\Graph::searchAndSort()
 | 
			
		||||
   * @param array $expected_reverse_paths
 | 
			
		||||
   *   An associative array containing vertices with their expected reverse
 | 
			
		||||
   *   paths.
 | 
			
		||||
   *
 | 
			
		||||
   * @internal
 | 
			
		||||
   */
 | 
			
		||||
  protected function assertReversePaths(array $graph, array $expected_reverse_paths): void {
 | 
			
		||||
    foreach ($expected_reverse_paths as $vertex => $paths) {
 | 
			
		||||
      // Build an array with keys = $paths and values = TRUE.
 | 
			
		||||
      $expected = array_fill_keys($paths, TRUE);
 | 
			
		||||
      $result = $graph[$vertex]['reverse_paths'] ?? [];
 | 
			
		||||
      $this->assertEquals($expected, $result, sprintf('Expected reverse paths for vertex %s: %s, got %s', $vertex, $this->displayArray($expected, TRUE), $this->displayArray($result, TRUE)));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Verify expected components in a graph.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $graph
 | 
			
		||||
   *   A graph array processed by
 | 
			
		||||
   *   \Drupal\Component\Graph\Graph::searchAndSort().
 | 
			
		||||
   * @param array $expected_components
 | 
			
		||||
   *   An array containing of components defined as a list of their vertices.
 | 
			
		||||
   *
 | 
			
		||||
   * @internal
 | 
			
		||||
   */
 | 
			
		||||
  protected function assertComponents(array $graph, array $expected_components): void {
 | 
			
		||||
    $unassigned_vertices = array_fill_keys(array_keys($graph), TRUE);
 | 
			
		||||
    foreach ($expected_components as $component) {
 | 
			
		||||
      $result_components = [];
 | 
			
		||||
      foreach ($component as $vertex) {
 | 
			
		||||
        $result_components[] = $graph[$vertex]['component'];
 | 
			
		||||
        unset($unassigned_vertices[$vertex]);
 | 
			
		||||
      }
 | 
			
		||||
      $this->assertCount(1, array_unique($result_components), sprintf('Expected one unique component for vertices %s, got %s', $this->displayArray($component), $this->displayArray($result_components)));
 | 
			
		||||
    }
 | 
			
		||||
    $this->assertEquals([], $unassigned_vertices, sprintf('Vertices not assigned to a component: %s', $this->displayArray($unassigned_vertices, TRUE)));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Verify expected order in a graph.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $graph
 | 
			
		||||
   *   A graph array processed by \Drupal\Component\Graph\Graph::searchAndSort()
 | 
			
		||||
   * @param array $expected_orders
 | 
			
		||||
   *   An array containing lists of vertices in their expected order.
 | 
			
		||||
   *
 | 
			
		||||
   * @internal
 | 
			
		||||
   */
 | 
			
		||||
  protected function assertWeights(array $graph, array $expected_orders): void {
 | 
			
		||||
    foreach ($expected_orders as $order) {
 | 
			
		||||
      $previous_vertex = array_shift($order);
 | 
			
		||||
      foreach ($order as $vertex) {
 | 
			
		||||
        $this->assertLessThan($graph[$vertex]['weight'], $graph[$previous_vertex]['weight'], sprintf("Weight of vertex %s should be less than vertex %s.", $previous_vertex, $vertex));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper function to output vertices as comma-separated list.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $paths
 | 
			
		||||
   *   An array containing a list of vertices.
 | 
			
		||||
   * @param bool $keys
 | 
			
		||||
   *   (optional) Whether to output the keys of $paths instead of the values.
 | 
			
		||||
   */
 | 
			
		||||
  protected function displayArray($paths, $keys = FALSE): string {
 | 
			
		||||
    if (!empty($paths)) {
 | 
			
		||||
      return implode(', ', $keys ? array_keys($paths) : $paths);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      return '(empty)';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,61 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\HttpFoundation;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\HttpFoundation\SecuredRedirectResponse;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Symfony\Component\HttpFoundation\Cookie;
 | 
			
		||||
use Symfony\Component\HttpFoundation\RedirectResponse;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test secure redirect base class.
 | 
			
		||||
 *
 | 
			
		||||
 * @group Routing
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\HttpFoundation\SecuredRedirectResponse
 | 
			
		||||
 */
 | 
			
		||||
class SecuredRedirectResponseTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests copying of redirect response.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::createFromRedirectResponse
 | 
			
		||||
   * @covers ::fromResponse
 | 
			
		||||
   */
 | 
			
		||||
  public function testRedirectCopy(): void {
 | 
			
		||||
    $redirect = new RedirectResponse('/magic_redirect_url', 301, ['x-cache-foobar' => 123]);
 | 
			
		||||
    $redirect->setProtocolVersion('2.0');
 | 
			
		||||
    $redirect->setCharset('ibm-943_P14A-2000');
 | 
			
		||||
    $redirect->headers->setCookie(new Cookie('name', 'value', 0, '/', NULL, FALSE, TRUE, FALSE, NULL));
 | 
			
		||||
 | 
			
		||||
    // Make a cloned redirect.
 | 
			
		||||
    $secureRedirect = SecuredRedirectStub::createFromRedirectResponse($redirect);
 | 
			
		||||
    $this->assertEquals('/magic_redirect_url', $secureRedirect->getTargetUrl());
 | 
			
		||||
    $this->assertEquals(301, $secureRedirect->getStatusCode());
 | 
			
		||||
    // We pull the headers from the original redirect because there are default
 | 
			
		||||
    // headers applied.
 | 
			
		||||
    $headers1 = $redirect->headers->all();
 | 
			
		||||
    $headers2 = $secureRedirect->headers->all();
 | 
			
		||||
    $this->assertEquals($headers1, $headers2);
 | 
			
		||||
    $this->assertEquals('2.0', $secureRedirect->getProtocolVersion());
 | 
			
		||||
    $this->assertEquals('ibm-943_P14A-2000', $secureRedirect->getCharset());
 | 
			
		||||
    $this->assertEquals($redirect->headers->getCookies(), $secureRedirect->headers->getCookies());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Test class for safe redirects.
 | 
			
		||||
 */
 | 
			
		||||
class SecuredRedirectStub extends SecuredRedirectResponse {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function isSafe($url): bool {
 | 
			
		||||
    // Empty implementation for testing.
 | 
			
		||||
    return TRUE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,111 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\PhpStorage;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\PhpStorage\FileStorage;
 | 
			
		||||
use Drupal\Component\PhpStorage\FileReadOnlyStorage;
 | 
			
		||||
use Drupal\Component\Utility\Random;
 | 
			
		||||
use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\PhpStorage\FileReadOnlyStorage
 | 
			
		||||
 *
 | 
			
		||||
 * @group Drupal
 | 
			
		||||
 * @group PhpStorage
 | 
			
		||||
 */
 | 
			
		||||
class FileStorageReadOnlyTest extends PhpStorageTestBase {
 | 
			
		||||
 | 
			
		||||
  use ExpectDeprecationTrait;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Standard test settings to pass to storage instances.
 | 
			
		||||
   *
 | 
			
		||||
   * @var array
 | 
			
		||||
   */
 | 
			
		||||
  protected $standardSettings;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Read only test settings to pass to storage instances.
 | 
			
		||||
   *
 | 
			
		||||
   * @var array
 | 
			
		||||
   */
 | 
			
		||||
  protected $readonlyStorage;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    $this->standardSettings = [
 | 
			
		||||
      'directory' => $this->directory,
 | 
			
		||||
      'bin' => 'test',
 | 
			
		||||
    ];
 | 
			
		||||
    $this->readonlyStorage = [
 | 
			
		||||
      'directory' => $this->directory,
 | 
			
		||||
      // Let this read from the bin where the other instance is writing.
 | 
			
		||||
      'bin' => 'test',
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests writing with one class and reading with another.
 | 
			
		||||
   */
 | 
			
		||||
  public function testReadOnly(): void {
 | 
			
		||||
    // Random generator.
 | 
			
		||||
    $random = new Random();
 | 
			
		||||
 | 
			
		||||
    $php = new FileStorage($this->standardSettings);
 | 
			
		||||
    $name = $random->name(8, TRUE) . '/' . $random->name(8, TRUE) . '.php';
 | 
			
		||||
 | 
			
		||||
    // Find a global that doesn't exist.
 | 
			
		||||
    do {
 | 
			
		||||
      $random = 'test' . mt_rand(10000, 100000);
 | 
			
		||||
    } while (isset($GLOBALS[$random]));
 | 
			
		||||
 | 
			
		||||
    // Write out a PHP file and ensure it's successfully loaded.
 | 
			
		||||
    $code = "<?php\n\$GLOBALS['$random'] = TRUE;";
 | 
			
		||||
    $success = $php->save($name, $code);
 | 
			
		||||
    $this->assertTrue($success);
 | 
			
		||||
    $php_read = new FileReadOnlyStorage($this->readonlyStorage);
 | 
			
		||||
    $php_read->load($name);
 | 
			
		||||
    $this->assertTrue($GLOBALS[$random]);
 | 
			
		||||
 | 
			
		||||
    // If the file was successfully loaded, it must also exist, but ensure the
 | 
			
		||||
    // exists() method returns that correctly.
 | 
			
		||||
    $this->assertTrue($php_read->exists($name));
 | 
			
		||||
    // Saving and deleting should always fail.
 | 
			
		||||
    $this->assertFalse($php_read->save($name, $code));
 | 
			
		||||
    $this->assertFalse($php_read->delete($name));
 | 
			
		||||
    unset($GLOBALS[$random]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::deleteAll
 | 
			
		||||
   */
 | 
			
		||||
  public function testDeleteAll(): void {
 | 
			
		||||
    // Random generator.
 | 
			
		||||
    $random = new Random();
 | 
			
		||||
 | 
			
		||||
    $php = new FileStorage($this->standardSettings);
 | 
			
		||||
    $name = $random->name(8, TRUE) . '/' . $random->name(8, TRUE) . '.php';
 | 
			
		||||
 | 
			
		||||
    // Find a global that doesn't exist.
 | 
			
		||||
    do {
 | 
			
		||||
      $random = mt_rand(10000, 100000);
 | 
			
		||||
    } while (isset($GLOBALS[$random]));
 | 
			
		||||
 | 
			
		||||
    // Write our the file so we can test deleting.
 | 
			
		||||
    $code = "<?php\n\$GLOBALS[$random] = TRUE;";
 | 
			
		||||
    $this->assertTrue($php->save($name, $code));
 | 
			
		||||
 | 
			
		||||
    $php_read = new FileReadOnlyStorage($this->readonlyStorage);
 | 
			
		||||
    $this->assertFalse($php_read->deleteAll());
 | 
			
		||||
 | 
			
		||||
    // Make sure directory exists prior to removal.
 | 
			
		||||
    $this->assertDirectoryExists($this->directory . '/test');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,115 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\PhpStorage;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\PhpStorage\FileStorage;
 | 
			
		||||
use Drupal\Component\Utility\Random;
 | 
			
		||||
use Drupal\TestTools\Extension\DeprecationBridge\ExpectDeprecationTrait;
 | 
			
		||||
use org\bovigo\vfs\vfsStreamDirectory;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\PhpStorage\FileStorage
 | 
			
		||||
 * @group Drupal
 | 
			
		||||
 * @group PhpStorage
 | 
			
		||||
 */
 | 
			
		||||
class FileStorageTest extends PhpStorageTestBase {
 | 
			
		||||
 | 
			
		||||
  use ExpectDeprecationTrait;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Standard test settings to pass to storage instances.
 | 
			
		||||
   *
 | 
			
		||||
   * @var array
 | 
			
		||||
   */
 | 
			
		||||
  protected $standardSettings;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    $this->standardSettings = [
 | 
			
		||||
      'directory' => $this->directory,
 | 
			
		||||
      'bin' => 'test',
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests basic load/save/delete operations.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::load
 | 
			
		||||
   * @covers ::save
 | 
			
		||||
   * @covers ::exists
 | 
			
		||||
   * @covers ::delete
 | 
			
		||||
   */
 | 
			
		||||
  public function testCRUD(): void {
 | 
			
		||||
    $php = new FileStorage($this->standardSettings);
 | 
			
		||||
    $this->assertCRUD($php);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::deleteAll
 | 
			
		||||
   */
 | 
			
		||||
  public function testDeleteAll(): void {
 | 
			
		||||
    // Random generator.
 | 
			
		||||
    $random_generator = new Random();
 | 
			
		||||
 | 
			
		||||
    // Write out some files.
 | 
			
		||||
    $php = new FileStorage($this->standardSettings);
 | 
			
		||||
 | 
			
		||||
    $name = $random_generator->name(8, TRUE) . '/' . $random_generator->name(8, TRUE) . '.php';
 | 
			
		||||
 | 
			
		||||
    // Find a global that doesn't exist.
 | 
			
		||||
    do {
 | 
			
		||||
      $random = 'test' . mt_rand(10000, 100000);
 | 
			
		||||
    } while (isset($GLOBALS[$random]));
 | 
			
		||||
 | 
			
		||||
    // Write out a PHP file and ensure it's successfully loaded.
 | 
			
		||||
    $code = "<?php\n\$GLOBALS['$random'] = TRUE;";
 | 
			
		||||
    $this->assertTrue($php->save($name, $code), 'Saved php file');
 | 
			
		||||
    $php->load($name);
 | 
			
		||||
    $this->assertTrue($GLOBALS[$random], 'File saved correctly with correct value');
 | 
			
		||||
 | 
			
		||||
    // Make sure directory exists prior to removal.
 | 
			
		||||
    $this->assertDirectoryExists($this->directory . '/test');
 | 
			
		||||
 | 
			
		||||
    $this->assertTrue($php->deleteAll(), 'Delete all reported success');
 | 
			
		||||
    $this->assertFalse($php->load($name));
 | 
			
		||||
    $this->assertDirectoryDoesNotExist($this->directory . '/test');
 | 
			
		||||
 | 
			
		||||
    // Should still return TRUE if directory has already been deleted.
 | 
			
		||||
    $this->assertTrue($php->deleteAll(), 'Delete all succeeds with nothing to delete');
 | 
			
		||||
    unset($GLOBALS[$random]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::createDirectory
 | 
			
		||||
   */
 | 
			
		||||
  public function testCreateDirectoryFailWarning(): void {
 | 
			
		||||
    $directory = new vfsStreamDirectory('permissionDenied', 0200);
 | 
			
		||||
    $storage = new FileStorage([
 | 
			
		||||
      'directory' => $directory->url(),
 | 
			
		||||
      'bin' => 'test',
 | 
			
		||||
    ]);
 | 
			
		||||
    $code = "<?php\n echo 'here';";
 | 
			
		||||
 | 
			
		||||
    // PHPUnit 10 cannot expect warnings, so we have to catch them ourselves.
 | 
			
		||||
    $messages = [];
 | 
			
		||||
    set_error_handler(function (int $errno, string $errstr) use (&$messages): void {
 | 
			
		||||
      $messages[] = [$errno, $errstr];
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $storage->save('subdirectory/foo.php', $code);
 | 
			
		||||
 | 
			
		||||
    restore_error_handler();
 | 
			
		||||
    $this->assertCount(2, $messages);
 | 
			
		||||
    $this->assertSame(E_USER_WARNING, $messages[0][0]);
 | 
			
		||||
    $this->assertSame('mkdir(): Permission Denied', $messages[0][1]);
 | 
			
		||||
    $this->assertSame(E_WARNING, $messages[1][0]);
 | 
			
		||||
    $this->assertStringStartsWith('file_put_contents(vfs://permissionDenied/test/subdirectory/foo.php)', $messages[1][1]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,35 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\PhpStorage;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests the MTimeProtectedFastFileStorage implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage
 | 
			
		||||
 *
 | 
			
		||||
 * @group Drupal
 | 
			
		||||
 * @group PhpStorage
 | 
			
		||||
 */
 | 
			
		||||
class MTimeProtectedFastFileStorageTest extends MTimeProtectedFileStorageBase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The expected test results for the security test.
 | 
			
		||||
   *
 | 
			
		||||
   * The first iteration does not change the directory mtime so this class will
 | 
			
		||||
   * include the hacked file on the first try but the second test will change
 | 
			
		||||
   * the directory mtime and so on the second try the file will not be included.
 | 
			
		||||
   *
 | 
			
		||||
   * @var bool[]
 | 
			
		||||
   */
 | 
			
		||||
  protected array $expected = [TRUE, FALSE];
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The PHP storage class to test.
 | 
			
		||||
   *
 | 
			
		||||
   * @var class-string
 | 
			
		||||
   */
 | 
			
		||||
  protected $storageClass = 'Drupal\Component\PhpStorage\MTimeProtectedFastFileStorage';
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,137 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\PhpStorage;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\FileSecurity\FileSecurity;
 | 
			
		||||
use Drupal\Component\Utility\Crypt;
 | 
			
		||||
use Drupal\Component\Utility\Random;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Base test class for MTime protected storage.
 | 
			
		||||
 */
 | 
			
		||||
abstract class MTimeProtectedFileStorageBase extends PhpStorageTestBase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The PHP storage class to test.
 | 
			
		||||
   *
 | 
			
		||||
   * This should be overridden by extending classes.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   */
 | 
			
		||||
  protected $storageClass;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The secret string to use for file creation.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   */
 | 
			
		||||
  protected $secret;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Test settings to pass to storage instances.
 | 
			
		||||
   *
 | 
			
		||||
   * @var array
 | 
			
		||||
   */
 | 
			
		||||
  protected $settings;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The expected test results for the security test.
 | 
			
		||||
   */
 | 
			
		||||
  protected array $expected;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    // Random generator.
 | 
			
		||||
    $random = new Random();
 | 
			
		||||
 | 
			
		||||
    $this->secret = $random->name(8, TRUE);
 | 
			
		||||
 | 
			
		||||
    $this->settings = [
 | 
			
		||||
      'directory' => $this->directory,
 | 
			
		||||
      'bin' => 'test',
 | 
			
		||||
      'secret' => $this->secret,
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests basic load/save/delete operations.
 | 
			
		||||
   */
 | 
			
		||||
  public function testCRUD(): void {
 | 
			
		||||
    $php = new $this->storageClass($this->settings);
 | 
			
		||||
    $this->assertCRUD($php);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests the security of the MTimeProtectedFileStorage implementation.
 | 
			
		||||
   *
 | 
			
		||||
   * We test two attacks: first changes the file mtime, then the directory
 | 
			
		||||
   * mtime too.
 | 
			
		||||
   *
 | 
			
		||||
   * We need to delay over 1 second for mtime test.
 | 
			
		||||
   *
 | 
			
		||||
   * @medium
 | 
			
		||||
   */
 | 
			
		||||
  public function testSecurity(): void {
 | 
			
		||||
    $php = new $this->storageClass($this->settings);
 | 
			
		||||
    $name = 'test.php';
 | 
			
		||||
    $php->save($name, '<?php');
 | 
			
		||||
    $expected_root_directory = $this->directory . '/test';
 | 
			
		||||
    if (str_ends_with($name, '.php')) {
 | 
			
		||||
      $expected_directory = $expected_root_directory . '/' . substr($name, 0, -4);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      $expected_directory = $expected_root_directory . '/' . $name;
 | 
			
		||||
    }
 | 
			
		||||
    $directory_mtime = filemtime($expected_directory);
 | 
			
		||||
    $expected_filename = $expected_directory . '/' . Crypt::hmacBase64($name, $this->secret . $directory_mtime) . '.php';
 | 
			
		||||
 | 
			
		||||
    // Ensure the file exists and that it and the containing directory have
 | 
			
		||||
    // minimal permissions. fileperms() can return high bits unrelated to
 | 
			
		||||
    // permissions, so mask with 0777.
 | 
			
		||||
    $this->assertFileExists($expected_filename);
 | 
			
		||||
    $this->assertSame(0444, fileperms($expected_filename) & 0777);
 | 
			
		||||
    $this->assertSame(0777, fileperms($expected_directory) & 0777);
 | 
			
		||||
 | 
			
		||||
    // Ensure the root directory for the bin has a .htaccess file denying web
 | 
			
		||||
    // access.
 | 
			
		||||
    $this->assertSame(file_get_contents($expected_root_directory . '/.htaccess'), FileSecurity::htaccessLines());
 | 
			
		||||
 | 
			
		||||
    // Ensure that if the file is replaced with an untrusted one (due to another
 | 
			
		||||
    // script's file upload vulnerability), it does not get loaded. Since mtime
 | 
			
		||||
    // granularity is 1 second, we cannot prevent an attack that happens within
 | 
			
		||||
    // a second of the initial save().
 | 
			
		||||
    sleep(1);
 | 
			
		||||
    for ($i = 0; $i < 2; $i++) {
 | 
			
		||||
      $php = new $this->storageClass($this->settings);
 | 
			
		||||
      $GLOBALS['hacked'] = FALSE;
 | 
			
		||||
      $untrusted_code = "<?php\n" . '$GLOBALS["hacked"] = TRUE;';
 | 
			
		||||
      chmod($expected_directory, 0700);
 | 
			
		||||
      chmod($expected_filename, 0700);
 | 
			
		||||
      if ($i) {
 | 
			
		||||
        // Now try to write the file in such a way that the directory mtime
 | 
			
		||||
        // changes and invalidates the hash.
 | 
			
		||||
        file_put_contents($expected_filename . '.tmp', $untrusted_code);
 | 
			
		||||
        rename($expected_filename . '.tmp', $expected_filename);
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        // On the first try do not change the directory mtime but the filemtime
 | 
			
		||||
        // is now larger than the directory mtime.
 | 
			
		||||
        file_put_contents($expected_filename, $untrusted_code);
 | 
			
		||||
      }
 | 
			
		||||
      chmod($expected_filename, 0400);
 | 
			
		||||
      chmod($expected_directory, 0100);
 | 
			
		||||
      $this->assertSame(file_get_contents($expected_filename), $untrusted_code);
 | 
			
		||||
      $this->assertSame($this->expected[$i], $php->exists($name));
 | 
			
		||||
      $this->assertSame($this->expected[$i], $php->load($name));
 | 
			
		||||
      $this->assertSame($this->expected[$i], $GLOBALS['hacked']);
 | 
			
		||||
    }
 | 
			
		||||
    unset($GLOBALS['hacked']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,34 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\PhpStorage;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests the MTimeProtectedFileStorage implementation.
 | 
			
		||||
 *
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\PhpStorage\MTimeProtectedFileStorage
 | 
			
		||||
 *
 | 
			
		||||
 * @group Drupal
 | 
			
		||||
 * @group PhpStorage
 | 
			
		||||
 */
 | 
			
		||||
class MTimeProtectedFileStorageTest extends MTimeProtectedFileStorageBase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The expected test results for the security test.
 | 
			
		||||
   *
 | 
			
		||||
   * The default implementation protects against even the filemtime change so
 | 
			
		||||
   * both iterations will return FALSE.
 | 
			
		||||
   *
 | 
			
		||||
   * @var bool[]
 | 
			
		||||
   */
 | 
			
		||||
  protected array $expected = [FALSE, FALSE];
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The PHP storage class to test.
 | 
			
		||||
   *
 | 
			
		||||
   * @var class-string
 | 
			
		||||
   */
 | 
			
		||||
  protected $storageClass = 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage';
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,84 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\PhpStorage;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\PhpStorage\PhpStorageInterface;
 | 
			
		||||
use Drupal\Component\Utility\Random;
 | 
			
		||||
use org\bovigo\vfs\vfsStream;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Base test for PHP storages.
 | 
			
		||||
 */
 | 
			
		||||
abstract class PhpStorageTestBase extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * A unique per test class directory path to test php storage.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   */
 | 
			
		||||
  protected $directory;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
    vfsStream::setup('exampleDir');
 | 
			
		||||
    $this->directory = vfsStream::url('exampleDir');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Assert that a PHP storage's load/save/delete operations work.
 | 
			
		||||
   */
 | 
			
		||||
  public function assertCRUD($php) {
 | 
			
		||||
    // Random generator.
 | 
			
		||||
    $random_generator = new Random();
 | 
			
		||||
 | 
			
		||||
    $name = $random_generator->name(8, TRUE) . '/' . $random_generator->name(8, TRUE) . '.php';
 | 
			
		||||
 | 
			
		||||
    // Find a global that doesn't exist.
 | 
			
		||||
    do {
 | 
			
		||||
      $random = 'test' . mt_rand(10000, 100000);
 | 
			
		||||
    } while (isset($GLOBALS[$random]));
 | 
			
		||||
 | 
			
		||||
    // Write out a PHP file and ensure it's successfully loaded.
 | 
			
		||||
    $code = "<?php\n\$GLOBALS['$random'] = TRUE;";
 | 
			
		||||
    $success = $php->save($name, $code);
 | 
			
		||||
    $this->assertTrue($success, 'Saved php file');
 | 
			
		||||
    $php->load($name);
 | 
			
		||||
    $this->assertTrue($GLOBALS[$random], 'File saved correctly with correct value');
 | 
			
		||||
 | 
			
		||||
    // Run additional asserts.
 | 
			
		||||
    $this->additionalAssertCRUD($php, $name);
 | 
			
		||||
 | 
			
		||||
    // If the file was successfully loaded, it must also exist, but ensure the
 | 
			
		||||
    // exists() method returns that correctly.
 | 
			
		||||
    $this->assertTrue($php->exists($name), 'Exists works correctly');
 | 
			
		||||
 | 
			
		||||
    // Delete the file, and then ensure exists() returns FALSE.
 | 
			
		||||
    $this->assertTrue($php->delete($name), 'Delete succeeded');
 | 
			
		||||
    $this->assertFalse($php->exists($name), 'Delete deleted file');
 | 
			
		||||
 | 
			
		||||
    // Ensure delete() can be called on a non-existing file. It should return
 | 
			
		||||
    // FALSE, but not trigger errors.
 | 
			
		||||
    $this->assertFalse($php->delete($name), 'Delete fails on missing file');
 | 
			
		||||
    unset($GLOBALS[$random]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Additional asserts to be run.
 | 
			
		||||
   *
 | 
			
		||||
   * @param \Drupal\Component\PhpStorage\PhpStorageInterface $php
 | 
			
		||||
   *   The PHP storage object.
 | 
			
		||||
   * @param string $name
 | 
			
		||||
   *   The name of an object. It should exist in the storage.
 | 
			
		||||
   */
 | 
			
		||||
  protected function additionalAssertCRUD(PhpStorageInterface $php, $name) {
 | 
			
		||||
    // By default do not do any additional asserts. This is a way of extending
 | 
			
		||||
    // tests in contrib.
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,50 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Plugin\Attribute;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Plugin\Attribute\AttributeBase;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Plugin\Attribute\AttributeBase
 | 
			
		||||
 * @group Attribute
 | 
			
		||||
 */
 | 
			
		||||
class AttributeBaseTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getProvider
 | 
			
		||||
   * @covers ::setProvider
 | 
			
		||||
   */
 | 
			
		||||
  public function testSetProvider(): void {
 | 
			
		||||
    $plugin = new AttributeBaseStub(id: '1');
 | 
			
		||||
    $plugin->setProvider('example');
 | 
			
		||||
    $this->assertEquals('example', $plugin->getProvider());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getId
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetId(): void {
 | 
			
		||||
    $plugin = new AttributeBaseStub(id: 'example');
 | 
			
		||||
    $this->assertEquals('example', $plugin->getId());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getClass
 | 
			
		||||
   * @covers ::setClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testSetClass(): void {
 | 
			
		||||
    $plugin = new AttributeBaseStub(id: '1');
 | 
			
		||||
    $plugin->setClass('example');
 | 
			
		||||
    $this->assertEquals('example', $plugin->getClass());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * {@inheritdoc}
 | 
			
		||||
 */
 | 
			
		||||
class AttributeBaseStub extends AttributeBase {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,156 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Plugin\Attribute;
 | 
			
		||||
 | 
			
		||||
use Composer\Autoload\ClassLoader;
 | 
			
		||||
use Drupal\Component\Plugin\Discovery\AttributeClassDiscovery;
 | 
			
		||||
use Drupal\Component\FileCache\FileCacheFactory;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Plugin\Discovery\AttributeClassDiscovery
 | 
			
		||||
 * @covers \Drupal\Component\Discovery\MissingClassDetectionClassLoader
 | 
			
		||||
 * @group Attribute
 | 
			
		||||
 * @runTestsInSeparateProcesses
 | 
			
		||||
 */
 | 
			
		||||
class AttributeClassDiscoveryCachedTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    // Ensure FileCacheFactory::DISABLE_CACHE is *not* set, since we're testing
 | 
			
		||||
    // integration with the file cache.
 | 
			
		||||
    FileCacheFactory::setConfiguration([]);
 | 
			
		||||
    // Ensure that FileCacheFactory has a prefix.
 | 
			
		||||
    FileCacheFactory::setPrefix('prefix');
 | 
			
		||||
 | 
			
		||||
    // Normally the attribute classes would be autoloaded.
 | 
			
		||||
    include_once __DIR__ . '/../../../../../fixtures/plugins/CustomPlugin.php';
 | 
			
		||||
 | 
			
		||||
    $additionalClassLoader = new ClassLoader();
 | 
			
		||||
    $additionalClassLoader->addPsr4("com\\example\\PluginNamespace\\", __DIR__ . "/../../../../../fixtures/plugins/Plugin/PluginNamespace");
 | 
			
		||||
    $additionalClassLoader->register(TRUE);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that getDefinitions() retrieves the file cache correctly.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getDefinitions
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetDefinitions(): void {
 | 
			
		||||
    // Path to the classes which we'll discover and parse annotation.
 | 
			
		||||
    $discovery_path = __DIR__ . "/../../../../../fixtures/plugins/Plugin";
 | 
			
		||||
    // File path that should be discovered within that directory.
 | 
			
		||||
    $file_path = $discovery_path . '/PluginNamespace/AttributeDiscoveryTest1.php';
 | 
			
		||||
    // Define file paths within the directory that should not be discovered.
 | 
			
		||||
    $non_discoverable_file_paths = [
 | 
			
		||||
      $discovery_path . '/PluginNamespace/AttributeDiscoveryTest2.php',
 | 
			
		||||
      $discovery_path . '/PluginNamespace/AttributeDiscoveryTestMissingInterface.php',
 | 
			
		||||
      $discovery_path . '/PluginNamespace/AttributeDiscoveryTestMissingTrait.php',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    $discovery = new AttributeClassDiscovery(['com\example' => [$discovery_path]]);
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'discovery_test_1' => [
 | 
			
		||||
        'id' => 'discovery_test_1',
 | 
			
		||||
        'class' => 'com\example\PluginNamespace\AttributeDiscoveryTest1',
 | 
			
		||||
      ],
 | 
			
		||||
    ], $discovery->getDefinitions());
 | 
			
		||||
 | 
			
		||||
    // Gain access to the file cache.
 | 
			
		||||
    $ref_file_cache = new \ReflectionProperty($discovery, 'fileCache');
 | 
			
		||||
    /** @var \Drupal\Component\FileCache\FileCacheInterface $file_cache */
 | 
			
		||||
    $file_cache = $ref_file_cache->getValue($discovery);
 | 
			
		||||
 | 
			
		||||
    // The valid plugin definition should be cached.
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'id' => 'discovery_test_1',
 | 
			
		||||
      'class' => 'com\example\PluginNamespace\AttributeDiscoveryTest1',
 | 
			
		||||
    ], unserialize($file_cache->get($file_path)['content']));
 | 
			
		||||
 | 
			
		||||
    // The plugins that extend a missing class, implement a missing interface,
 | 
			
		||||
    // and use a missing trait should not be cached.
 | 
			
		||||
    foreach ($non_discoverable_file_paths as $non_discoverable_file_path) {
 | 
			
		||||
      $this->assertTrue(file_exists($non_discoverable_file_path));
 | 
			
		||||
      $this->assertNull($file_cache->get($non_discoverable_file_path));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Change the file cache entry.
 | 
			
		||||
    // The file cache is keyed by the file path, and we'll add some known
 | 
			
		||||
    // content to test against.
 | 
			
		||||
    $file_cache->set($file_path, [
 | 
			
		||||
      'id' => 'wrong_id',
 | 
			
		||||
      'content' => serialize(['an' => 'array']),
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    // Now perform the same query and check for the cached results.
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'wrong_id' => [
 | 
			
		||||
        'an' => 'array',
 | 
			
		||||
      ],
 | 
			
		||||
    ], $discovery->getDefinitions());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests discovery with missing traits.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getDefinitions
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetDefinitionsMissingTrait(): void {
 | 
			
		||||
    // Path to the classes which we'll discover and parse annotation.
 | 
			
		||||
    $discovery_path = __DIR__ . "/../../../../../fixtures/plugins/Plugin";
 | 
			
		||||
    // Define file paths within the directory that should not be discovered.
 | 
			
		||||
    $non_discoverable_file_paths = [
 | 
			
		||||
      $discovery_path . '/PluginNamespace/AttributeDiscoveryTest2.php',
 | 
			
		||||
      $discovery_path . '/PluginNamespace/AttributeDiscoveryTestMissingInterface.php',
 | 
			
		||||
      $discovery_path . '/PluginNamespace/AttributeDiscoveryTestMissingTrait.php',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    $discovery = new AttributeClassDiscovery(['com\example' => [$discovery_path]]);
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'discovery_test_1' => [
 | 
			
		||||
        'id' => 'discovery_test_1',
 | 
			
		||||
        'class' => 'com\example\PluginNamespace\AttributeDiscoveryTest1',
 | 
			
		||||
      ],
 | 
			
		||||
    ], $discovery->getDefinitions());
 | 
			
		||||
 | 
			
		||||
    // Gain access to the file cache.
 | 
			
		||||
    $ref_file_cache = new \ReflectionProperty($discovery, 'fileCache');
 | 
			
		||||
    /** @var \Drupal\Component\FileCache\FileCacheInterface $file_cache */
 | 
			
		||||
    $file_cache = $ref_file_cache->getValue($discovery);
 | 
			
		||||
 | 
			
		||||
    // The plugins that extend a missing class, implement a missing interface,
 | 
			
		||||
    // and use a missing trait should not be cached.
 | 
			
		||||
    foreach ($non_discoverable_file_paths as $non_discoverable_file_path) {
 | 
			
		||||
      $this->assertTrue(file_exists($non_discoverable_file_path));
 | 
			
		||||
      $this->assertNull($file_cache->get($non_discoverable_file_path));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $discovery = new AttributeClassDiscovery(['com\example' => [$discovery_path], 'Drupal\a_module_that_does_not_exist' => [$discovery_path]]);
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'discovery_test_1' => [
 | 
			
		||||
        'id' => 'discovery_test_1',
 | 
			
		||||
        'class' => 'com\example\PluginNamespace\AttributeDiscoveryTest1',
 | 
			
		||||
      ],
 | 
			
		||||
      'discovery_test_missing_trait' => [
 | 
			
		||||
        'id' => 'discovery_test_missing_trait',
 | 
			
		||||
        'class' => 'com\example\PluginNamespace\AttributeDiscoveryTestMissingTrait',
 | 
			
		||||
        'title' => 'Discovery test plugin missing trait',
 | 
			
		||||
      ],
 | 
			
		||||
    ], $discovery->getDefinitions());
 | 
			
		||||
 | 
			
		||||
    // The plugins that extend a missing class, implement a missing interface,
 | 
			
		||||
    // and use a missing trait should not be cached. This is the case even for
 | 
			
		||||
    // the plugin that was just discovered.
 | 
			
		||||
    foreach ($non_discoverable_file_paths as $non_discoverable_file_path) {
 | 
			
		||||
      $this->assertTrue(file_exists($non_discoverable_file_path));
 | 
			
		||||
      $this->assertNull($file_cache->get($non_discoverable_file_path));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,78 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Plugin\Attribute;
 | 
			
		||||
 | 
			
		||||
use Composer\Autoload\ClassLoader;
 | 
			
		||||
use Drupal\Component\Plugin\Discovery\AttributeClassDiscovery;
 | 
			
		||||
use Drupal\Component\FileCache\FileCacheFactory;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Plugin\Discovery\AttributeClassDiscovery
 | 
			
		||||
 * @group Attribute
 | 
			
		||||
 * @runTestsInSeparateProcesses
 | 
			
		||||
 */
 | 
			
		||||
class AttributeClassDiscoveryTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp(): void {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    // Ensure the file cache is disabled.
 | 
			
		||||
    FileCacheFactory::setConfiguration([FileCacheFactory::DISABLE_CACHE => TRUE]);
 | 
			
		||||
    // Ensure that FileCacheFactory has a prefix.
 | 
			
		||||
    FileCacheFactory::setPrefix('prefix');
 | 
			
		||||
 | 
			
		||||
    // Normally the attribute classes would be autoloaded.
 | 
			
		||||
    include_once __DIR__ . '/../../../../../fixtures/plugins/CustomPlugin.php';
 | 
			
		||||
 | 
			
		||||
    $additionalClassLoader = new ClassLoader();
 | 
			
		||||
    $additionalClassLoader->addPsr4("com\\example\\PluginNamespace\\", __DIR__ . "/../../../../../fixtures/plugins/Plugin/PluginNamespace");
 | 
			
		||||
    $additionalClassLoader->register(TRUE);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::__construct
 | 
			
		||||
   * @covers ::getPluginNamespaces
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetPluginNamespaces(): void {
 | 
			
		||||
    // Path to the classes which we'll discover and parse annotation.
 | 
			
		||||
    $discovery = new AttributeClassDiscovery(['com/example' => [__DIR__]]);
 | 
			
		||||
 | 
			
		||||
    $reflection = new \ReflectionMethod($discovery, 'getPluginNamespaces');
 | 
			
		||||
 | 
			
		||||
    $result = $reflection->invoke($discovery);
 | 
			
		||||
    $this->assertEquals(['com/example' => [__DIR__]], $result);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getDefinitions
 | 
			
		||||
   * @covers ::prepareAttributeDefinition
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetDefinitions(): void {
 | 
			
		||||
    $discovery = new AttributeClassDiscovery(['com\example' => [__DIR__ . "/../../../../../fixtures/plugins/Plugin"]]);
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'discovery_test_1' => [
 | 
			
		||||
        'id' => 'discovery_test_1',
 | 
			
		||||
        'class' => 'com\example\PluginNamespace\AttributeDiscoveryTest1',
 | 
			
		||||
      ],
 | 
			
		||||
    ], $discovery->getDefinitions());
 | 
			
		||||
 | 
			
		||||
    $custom_annotation_discovery = new AttributeClassDiscovery(['com\example' => [__DIR__ . "/../../../../../fixtures/plugins/Plugin"]], 'com\example\PluginNamespace\CustomPlugin');
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'discovery_test_1' => [
 | 
			
		||||
        'id' => 'discovery_test_1',
 | 
			
		||||
        'class' => 'com\example\PluginNamespace\AttributeDiscoveryTest1',
 | 
			
		||||
        'title' => 'Discovery test plugin',
 | 
			
		||||
      ],
 | 
			
		||||
    ], $custom_annotation_discovery->getDefinitions());
 | 
			
		||||
 | 
			
		||||
    $empty_discovery = new AttributeClassDiscovery(['com\example' => [__DIR__ . "/../../../../../fixtures/plugins/Plugin"]], 'com\example\PluginNamespace\CustomPlugin2');
 | 
			
		||||
    $this->assertEquals([], $empty_discovery->getDefinitions());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,40 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Plugin\Attribute;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Plugin\Attribute\PluginID;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Plugin\Attribute\PluginID
 | 
			
		||||
 * @group Attribute
 | 
			
		||||
 */
 | 
			
		||||
class PluginIdTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::get
 | 
			
		||||
   */
 | 
			
		||||
  public function testGet(): void {
 | 
			
		||||
    // Assert plugin starts with only an ID.
 | 
			
		||||
    $plugin = new PluginID(id: 'test');
 | 
			
		||||
    // Plugin's always have a class set by discovery.
 | 
			
		||||
    $plugin->setClass('bar');
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'id' => 'test',
 | 
			
		||||
      'class' => 'bar',
 | 
			
		||||
      'provider' => NULL,
 | 
			
		||||
    ], $plugin->get());
 | 
			
		||||
 | 
			
		||||
    // Set values and ensure we can retrieve them.
 | 
			
		||||
    $plugin->setClass('bar2');
 | 
			
		||||
    $plugin->setProvider('baz');
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'id' => 'test',
 | 
			
		||||
      'class' => 'bar2',
 | 
			
		||||
      'provider' => 'baz',
 | 
			
		||||
    ], $plugin->get());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,65 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Plugin\Attribute;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Plugin\Attribute\Plugin;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Plugin\Attribute\Plugin
 | 
			
		||||
 * @group Attribute
 | 
			
		||||
 */
 | 
			
		||||
class PluginTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::__construct
 | 
			
		||||
   * @covers ::get
 | 
			
		||||
   */
 | 
			
		||||
  public function testGet(): void {
 | 
			
		||||
    $plugin = new PluginStub(id: 'example', deriver: 'test');
 | 
			
		||||
    $plugin->setClass('foo');
 | 
			
		||||
    $this->assertEquals([
 | 
			
		||||
      'id' => 'example',
 | 
			
		||||
      'class' => 'foo',
 | 
			
		||||
      'deriver' => 'test',
 | 
			
		||||
    ], $plugin->get());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::setProvider
 | 
			
		||||
   * @covers ::getProvider
 | 
			
		||||
   */
 | 
			
		||||
  public function testSetProvider(): void {
 | 
			
		||||
    $plugin = new Plugin(id: 'example');
 | 
			
		||||
    $plugin->setProvider('example');
 | 
			
		||||
    $this->assertEquals('example', $plugin->getProvider());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getId
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetId(): void {
 | 
			
		||||
    $plugin = new Plugin(id: 'example');
 | 
			
		||||
    $this->assertEquals('example', $plugin->getId());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::setClass
 | 
			
		||||
   * @covers ::getClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testSetClass(): void {
 | 
			
		||||
    $plugin = new Plugin(id: 'test');
 | 
			
		||||
    $plugin->setClass('example');
 | 
			
		||||
    $this->assertEquals('example', $plugin->getClass());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * {@inheritdoc}
 | 
			
		||||
 */
 | 
			
		||||
class PluginStub extends Plugin {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,130 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Plugin\Context;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Plugin\Context\Context;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Plugin\Context\Context
 | 
			
		||||
 * @group Plugin
 | 
			
		||||
 */
 | 
			
		||||
class ContextTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider for testGetContextValue.
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerGetContextValue() {
 | 
			
		||||
    return [
 | 
			
		||||
      ['context_value', 'context_value', FALSE, 'data_type'],
 | 
			
		||||
      [NULL, NULL, FALSE, 'data_type'],
 | 
			
		||||
      ['will throw exception', NULL, TRUE, 'data_type'],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getContextValue
 | 
			
		||||
   * @dataProvider providerGetContextValue
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetContextValue($expected, $context_value, $is_required, $data_type): void {
 | 
			
		||||
    // Mock a Context object.
 | 
			
		||||
    $mock_context = $this->getMockBuilder('Drupal\Component\Plugin\Context\Context')
 | 
			
		||||
      ->disableOriginalConstructor()
 | 
			
		||||
      ->onlyMethods(['getContextDefinition'])
 | 
			
		||||
      ->getMock();
 | 
			
		||||
 | 
			
		||||
    // If the context value exists, getContextValue() behaves like a normal
 | 
			
		||||
    // getter.
 | 
			
		||||
    if ($context_value) {
 | 
			
		||||
      // Set visibility of contextValue.
 | 
			
		||||
      $ref_context_value = new \ReflectionProperty($mock_context, 'contextValue');
 | 
			
		||||
      // Set contextValue to a testable state.
 | 
			
		||||
      $ref_context_value->setValue($mock_context, $context_value);
 | 
			
		||||
      // Exercise getContextValue().
 | 
			
		||||
      $this->assertEquals($context_value, $mock_context->getContextValue());
 | 
			
		||||
    }
 | 
			
		||||
    // If no context value exists, we have to cover either returning NULL or
 | 
			
		||||
    // throwing an exception if the definition requires it.
 | 
			
		||||
    else {
 | 
			
		||||
      // Create a mock definition.
 | 
			
		||||
      $mock_definition = $this->createMock('Drupal\Component\Plugin\Context\ContextDefinitionInterface');
 | 
			
		||||
 | 
			
		||||
      // Set expectation for isRequired().
 | 
			
		||||
      $mock_definition->expects($this->once())
 | 
			
		||||
        ->method('isRequired')
 | 
			
		||||
        ->willReturn($is_required);
 | 
			
		||||
 | 
			
		||||
      // Set expectation for getDataType().
 | 
			
		||||
      $mock_definition->expects($this->exactly(
 | 
			
		||||
            $is_required ? 1 : 0
 | 
			
		||||
        ))
 | 
			
		||||
        ->method('getDataType')
 | 
			
		||||
        ->willReturn($data_type);
 | 
			
		||||
 | 
			
		||||
      // Set expectation for getContextDefinition().
 | 
			
		||||
      $mock_context->expects($this->once())
 | 
			
		||||
        ->method('getContextDefinition')
 | 
			
		||||
        ->willReturn($mock_definition);
 | 
			
		||||
 | 
			
		||||
      // Set expectation for exception.
 | 
			
		||||
      if ($is_required) {
 | 
			
		||||
        $this->expectException('Drupal\Component\Plugin\Exception\ContextException');
 | 
			
		||||
        $this->expectExceptionMessage(sprintf("The %s context is required and not present.", $data_type));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Exercise getContextValue().
 | 
			
		||||
      $this->assertEquals($context_value, $mock_context->getContextValue());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider for testHasContextValue.
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerHasContextValue() {
 | 
			
		||||
    return [
 | 
			
		||||
      [TRUE, FALSE],
 | 
			
		||||
      [TRUE, 0],
 | 
			
		||||
      [TRUE, -0],
 | 
			
		||||
      [TRUE, 0.0],
 | 
			
		||||
      [TRUE, -0.0],
 | 
			
		||||
      [TRUE, ''],
 | 
			
		||||
      [TRUE, '0'],
 | 
			
		||||
      [TRUE, []],
 | 
			
		||||
      [FALSE, NULL],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::hasContextValue
 | 
			
		||||
   * @dataProvider providerHasContextValue
 | 
			
		||||
   */
 | 
			
		||||
  public function testHasContextValue($has_context_value, $default_value): void {
 | 
			
		||||
    $mock_definition = $this->createMock('Drupal\Component\Plugin\Context\ContextDefinitionInterface');
 | 
			
		||||
 | 
			
		||||
    $mock_definition->expects($this->atLeastOnce())
 | 
			
		||||
      ->method('getDefaultValue')
 | 
			
		||||
      ->willReturn($default_value);
 | 
			
		||||
 | 
			
		||||
    $context = new Context($mock_definition);
 | 
			
		||||
 | 
			
		||||
    $this->assertSame($has_context_value, $context->hasContextValue());
 | 
			
		||||
    $this->assertSame($default_value, $context->getContextValue());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getContextValue
 | 
			
		||||
   */
 | 
			
		||||
  public function testDefaultValue(): void {
 | 
			
		||||
    $mock_definition = $this->createMock('Drupal\Component\Plugin\Context\ContextDefinitionInterface');
 | 
			
		||||
 | 
			
		||||
    $mock_definition->expects($this->once())
 | 
			
		||||
      ->method('getDefaultValue')
 | 
			
		||||
      ->willReturn('test');
 | 
			
		||||
 | 
			
		||||
    $context = new Context($mock_definition);
 | 
			
		||||
    $this->assertEquals('test', $context->getContextValue());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,153 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Plugin;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Plugin\Definition\PluginDefinitionInterface;
 | 
			
		||||
use Drupal\Component\Plugin\Exception\PluginException;
 | 
			
		||||
use Drupal\Component\Plugin\Factory\DefaultFactory;
 | 
			
		||||
use Drupal\Tests\Component\Plugin\Fixtures\vegetable\Broccoli;
 | 
			
		||||
use Drupal\Tests\Component\Plugin\Fixtures\vegetable\Corn;
 | 
			
		||||
use Drupal\Tests\Component\Plugin\Fixtures\vegetable\VegetableInterface;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Plugin\Factory\DefaultFactory
 | 
			
		||||
 * @group Plugin
 | 
			
		||||
 */
 | 
			
		||||
class DefaultFactoryTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests getPluginClass() with a valid array plugin definition.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getPluginClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetPluginClassWithValidArrayPluginDefinition(): void {
 | 
			
		||||
    $plugin_class = Corn::class;
 | 
			
		||||
    $class = DefaultFactory::getPluginClass('corn', ['class' => $plugin_class]);
 | 
			
		||||
 | 
			
		||||
    $this->assertEquals($plugin_class, $class);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests getPluginClass() with a valid object plugin definition.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getPluginClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetPluginClassWithValidObjectPluginDefinition(): void {
 | 
			
		||||
    $plugin_class = Corn::class;
 | 
			
		||||
    $plugin_definition = $this->getMockBuilder(PluginDefinitionInterface::class)->getMock();
 | 
			
		||||
    $plugin_definition->expects($this->atLeastOnce())
 | 
			
		||||
      ->method('getClass')
 | 
			
		||||
      ->willReturn($plugin_class);
 | 
			
		||||
    $class = DefaultFactory::getPluginClass('corn', $plugin_definition);
 | 
			
		||||
 | 
			
		||||
    $this->assertEquals($plugin_class, $class);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests getPluginClass() with a missing class definition.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getPluginClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetPluginClassWithMissingClassWithArrayPluginDefinition(): void {
 | 
			
		||||
    $this->expectException(PluginException::class);
 | 
			
		||||
    $this->expectExceptionMessage('The plugin (corn) did not specify an instance class.');
 | 
			
		||||
    DefaultFactory::getPluginClass('corn', []);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests getPluginClass() with a missing class definition.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getPluginClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetPluginClassWithMissingClassWithObjectPluginDefinition(): void {
 | 
			
		||||
    $plugin_definition = $this->getMockBuilder(PluginDefinitionInterface::class)
 | 
			
		||||
      ->getMock();
 | 
			
		||||
    $this->expectException(PluginException::class);
 | 
			
		||||
    $this->expectExceptionMessage('The plugin (corn) did not specify an instance class.');
 | 
			
		||||
    DefaultFactory::getPluginClass('corn', $plugin_definition);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests getPluginClass() with a non-existent class definition.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getPluginClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetPluginClassWithNotExistingClassWithArrayPluginDefinition(): void {
 | 
			
		||||
    $this->expectException(PluginException::class);
 | 
			
		||||
    $this->expectExceptionMessage('Plugin (carrot) instance class "Drupal\Tests\Component\Plugin\Fixtures\vegetable\Carrot" does not exist.');
 | 
			
		||||
    DefaultFactory::getPluginClass('carrot', ['class' => 'Drupal\Tests\Component\Plugin\Fixtures\vegetable\Carrot']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests getPluginClass() with a non-existent class definition.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getPluginClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetPluginClassWithNotExistingClassWithObjectPluginDefinition(): void {
 | 
			
		||||
    $plugin_class = 'Drupal\Tests\Component\Plugin\Fixtures\vegetable\Carrot';
 | 
			
		||||
    $plugin_definition = $this->getMockBuilder(PluginDefinitionInterface::class)->getMock();
 | 
			
		||||
    $plugin_definition->expects($this->atLeastOnce())
 | 
			
		||||
      ->method('getClass')
 | 
			
		||||
      ->willReturn($plugin_class);
 | 
			
		||||
    $this->expectException(PluginException::class);
 | 
			
		||||
    DefaultFactory::getPluginClass('carrot', $plugin_definition);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests getPluginClass() with a required interface.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getPluginClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetPluginClassWithInterfaceWithArrayPluginDefinition(): void {
 | 
			
		||||
    $plugin_class = Corn::class;
 | 
			
		||||
    $class = DefaultFactory::getPluginClass('corn', ['class' => $plugin_class], VegetableInterface::class);
 | 
			
		||||
 | 
			
		||||
    $this->assertEquals($plugin_class, $class);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests getPluginClass() with a required interface.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getPluginClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetPluginClassWithInterfaceWithObjectPluginDefinition(): void {
 | 
			
		||||
    $plugin_class = Corn::class;
 | 
			
		||||
    $plugin_definition = $this->getMockBuilder(PluginDefinitionInterface::class)->getMock();
 | 
			
		||||
    $plugin_definition->expects($this->atLeastOnce())
 | 
			
		||||
      ->method('getClass')
 | 
			
		||||
      ->willReturn($plugin_class);
 | 
			
		||||
    $class = DefaultFactory::getPluginClass('corn', $plugin_definition, VegetableInterface::class);
 | 
			
		||||
 | 
			
		||||
    $this->assertEquals($plugin_class, $class);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests getPluginClass() with a required interface but no implementation.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getPluginClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetPluginClassWithInterfaceAndInvalidClassWithArrayPluginDefinition(): void {
 | 
			
		||||
    $this->expectException(PluginException::class);
 | 
			
		||||
    $this->expectExceptionMessage('Plugin "corn" (Drupal\Tests\Component\Plugin\Fixtures\vegetable\Broccoli) must implement interface Drupal\Tests\Component\Plugin\Fixtures\vegetable\VegetableInterface.');
 | 
			
		||||
    DefaultFactory::getPluginClass('corn', ['class' => Broccoli::class], VegetableInterface::class);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests getPluginClass() with a required interface but no implementation.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::getPluginClass
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetPluginClassWithInterfaceAndInvalidClassWithObjectPluginDefinition(): void {
 | 
			
		||||
    $plugin_class = Broccoli::class;
 | 
			
		||||
    $plugin_definition = $this->getMockBuilder(PluginDefinitionInterface::class)->getMock();
 | 
			
		||||
    $plugin_definition->expects($this->atLeastOnce())
 | 
			
		||||
      ->method('getClass')
 | 
			
		||||
      ->willReturn($plugin_class);
 | 
			
		||||
    $this->expectException(PluginException::class);
 | 
			
		||||
    DefaultFactory::getPluginClass('corn', $plugin_definition, VegetableInterface::class);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,102 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Plugin\Discovery;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery;
 | 
			
		||||
use Drupal\Component\FileCache\FileCacheFactory;
 | 
			
		||||
use org\bovigo\vfs\vfsStream;
 | 
			
		||||
use org\bovigo\vfs\vfsStreamDirectory;
 | 
			
		||||
use org\bovigo\vfs\vfsStreamWrapper;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery
 | 
			
		||||
 *
 | 
			
		||||
 * @group Annotation
 | 
			
		||||
 * @group Plugin
 | 
			
		||||
 */
 | 
			
		||||
class AnnotatedClassDiscoveryTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * All the Drupal documentation standards tags.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string[]
 | 
			
		||||
   */
 | 
			
		||||
  public static function provideBadAnnotations() {
 | 
			
		||||
    return [
 | 
			
		||||
      ['addtogroup'],
 | 
			
		||||
      ['code'],
 | 
			
		||||
      ['defgroup'],
 | 
			
		||||
      ['deprecated'],
 | 
			
		||||
      ['endcode'],
 | 
			
		||||
      ['endlink'],
 | 
			
		||||
      ['file'],
 | 
			
		||||
      ['ingroup'],
 | 
			
		||||
      ['group'],
 | 
			
		||||
      ['link'],
 | 
			
		||||
      ['mainpage'],
 | 
			
		||||
      ['param'],
 | 
			
		||||
      ['ref'],
 | 
			
		||||
      ['return'],
 | 
			
		||||
      ['section'],
 | 
			
		||||
      ['see'],
 | 
			
		||||
      ['subsection'],
 | 
			
		||||
      ['throws'],
 | 
			
		||||
      ['todo'],
 | 
			
		||||
      ['var'],
 | 
			
		||||
      ['{'],
 | 
			
		||||
      ['}'],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Make sure AnnotatedClassDiscovery never tries to autoload bad annotations.
 | 
			
		||||
   *
 | 
			
		||||
   * @dataProvider provideBadAnnotations
 | 
			
		||||
   *
 | 
			
		||||
   * @coversNothing
 | 
			
		||||
   */
 | 
			
		||||
  public function testAutoloadBadAnnotations($annotation): void {
 | 
			
		||||
    // Set up a class file in vfsStream.
 | 
			
		||||
    vfsStreamWrapper::register();
 | 
			
		||||
    $root = new vfsStreamDirectory('root');
 | 
			
		||||
    vfsStreamWrapper::setRoot($root);
 | 
			
		||||
 | 
			
		||||
    FileCacheFactory::setPrefix(__CLASS__);
 | 
			
		||||
 | 
			
		||||
    // Make a directory for discovery.
 | 
			
		||||
    $url = vfsStream::url('root');
 | 
			
		||||
    mkdir($url . '/DrupalTest');
 | 
			
		||||
 | 
			
		||||
    // Create a class docblock with our annotation.
 | 
			
		||||
    $php_file = "<?php\nnamespace DrupalTest;\n/**\n";
 | 
			
		||||
    $php_file .= " * @$annotation\n";
 | 
			
		||||
    $php_file .= " */\nclass TestClass {}";
 | 
			
		||||
    file_put_contents($url . '/DrupalTest/TestClass.php', $php_file);
 | 
			
		||||
 | 
			
		||||
    // Create an AnnotatedClassDiscovery object referencing the virtual file.
 | 
			
		||||
    $discovery = new AnnotatedClassDiscovery(
 | 
			
		||||
      ['\\DrupalTest\\TestClass' => [vfsStream::url('root/DrupalTest')]], '\\DrupalTest\\Component\\Annotation\\'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Register our class loader which will fail if the annotation reader tries
 | 
			
		||||
    // to autoload disallowed annotations.
 | 
			
		||||
    $class_loader = function ($class_name) use ($annotation) {
 | 
			
		||||
      $name_array = explode('\\', $class_name);
 | 
			
		||||
      $name = array_pop($name_array);
 | 
			
		||||
      if ($name == $annotation) {
 | 
			
		||||
        $this->fail('Attempted to autoload a non-plugin annotation: ' . $name);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    spl_autoload_register($class_loader, TRUE, TRUE);
 | 
			
		||||
    // Now try to get plugin definitions.
 | 
			
		||||
    $definitions = $discovery->getDefinitions();
 | 
			
		||||
    // Unregister to clean up.
 | 
			
		||||
    spl_autoload_unregister($class_loader);
 | 
			
		||||
    // Assert that no annotations were loaded.
 | 
			
		||||
    $this->assertEmpty($definitions);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,110 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Plugin\Discovery;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Plugin\Attribute\Plugin;
 | 
			
		||||
use Drupal\Component\Plugin\Definition\PluginDefinition;
 | 
			
		||||
use Drupal\Component\Plugin\Discovery\AttributeBridgeDecorator;
 | 
			
		||||
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Annotation\Plugin\Discovery\AnnotationBridgeDecorator
 | 
			
		||||
 * @group Plugin
 | 
			
		||||
 */
 | 
			
		||||
class AttributeBridgeDecoratorTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getDefinitions
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetDefinitions(): void {
 | 
			
		||||
    // Normally the attribute classes would be autoloaded.
 | 
			
		||||
    include_once __DIR__ . '/../../../../../fixtures/plugins/CustomPlugin.php';
 | 
			
		||||
    include_once __DIR__ . '/../../../../../fixtures/plugins/Plugin/PluginNamespace/AttributeDiscoveryTest1.php';
 | 
			
		||||
 | 
			
		||||
    $definitions = [];
 | 
			
		||||
    $definitions['object'] = new ObjectDefinition(['id' => 'foo']);
 | 
			
		||||
    $definitions['array'] = [
 | 
			
		||||
      'id' => 'bar',
 | 
			
		||||
      'class' => 'com\example\PluginNamespace\AttributeDiscoveryTest1',
 | 
			
		||||
    ];
 | 
			
		||||
    $discovery = $this->createMock(DiscoveryInterface::class);
 | 
			
		||||
    $discovery->expects($this->any())
 | 
			
		||||
      ->method('getDefinitions')
 | 
			
		||||
      ->willReturn($definitions);
 | 
			
		||||
 | 
			
		||||
    $decorator = new AttributeBridgeDecorator($discovery, TestAttribute::class);
 | 
			
		||||
 | 
			
		||||
    $expected = [
 | 
			
		||||
      'object' => new ObjectDefinition(['id' => 'foo']),
 | 
			
		||||
      'array' => (new ObjectDefinition(['id' => 'bar']))->setClass('com\example\PluginNamespace\AttributeDiscoveryTest1'),
 | 
			
		||||
    ];
 | 
			
		||||
    $this->assertEquals($expected, $decorator->getDefinitions());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests that the decorator of other methods works.
 | 
			
		||||
   *
 | 
			
		||||
   * @covers ::__call
 | 
			
		||||
   */
 | 
			
		||||
  public function testOtherMethod(): void {
 | 
			
		||||
    // Normally the attribute classes would be autoloaded.
 | 
			
		||||
    include_once __DIR__ . '/../../../../../fixtures/plugins/CustomPlugin.php';
 | 
			
		||||
    include_once __DIR__ . '/../../../../../fixtures/plugins/Plugin/PluginNamespace/AttributeDiscoveryTest1.php';
 | 
			
		||||
 | 
			
		||||
    $discovery = $this->createMock(ExtendedDiscoveryInterface::class);
 | 
			
		||||
    $discovery->expects($this->exactly(2))
 | 
			
		||||
      ->method('otherMethod')
 | 
			
		||||
      ->willReturnCallback(fn($id) => $id === 'foo');
 | 
			
		||||
 | 
			
		||||
    $decorator = new AttributeBridgeDecorator($discovery, TestAttribute::class);
 | 
			
		||||
 | 
			
		||||
    $this->assertTrue($decorator->otherMethod('foo'));
 | 
			
		||||
    $this->assertFalse($decorator->otherMethod('bar'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An interface for testing the Discovery interface.
 | 
			
		||||
 */
 | 
			
		||||
interface ExtendedDiscoveryInterface extends DiscoveryInterface {
 | 
			
		||||
 | 
			
		||||
  public function otherMethod(string $id): bool;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * {@inheritdoc}
 | 
			
		||||
 */
 | 
			
		||||
class TestAttribute extends Plugin {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function get(): object {
 | 
			
		||||
    return new ObjectDefinition(parent::get());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * {@inheritdoc}
 | 
			
		||||
 */
 | 
			
		||||
class ObjectDefinition extends PluginDefinition {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * ObjectDefinition constructor.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $definition
 | 
			
		||||
   *   An array of definition values.
 | 
			
		||||
   */
 | 
			
		||||
  public function __construct(array $definition) {
 | 
			
		||||
    foreach ($definition as $property => $value) {
 | 
			
		||||
      $this->{$property} = $value;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,81 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Tests\Component\Plugin\Discovery;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @coversDefaultClass \Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait
 | 
			
		||||
 * @uses \Drupal\Component\Plugin\Discovery\DiscoveryTrait
 | 
			
		||||
 * @group Plugin
 | 
			
		||||
 */
 | 
			
		||||
class DiscoveryCachedTraitTest extends TestCase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Data provider for testGetDefinition().
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   - Expected result from getDefinition().
 | 
			
		||||
   *   - Cached definitions to be placed into self::$definitions
 | 
			
		||||
   *   - Definitions to be returned by getDefinitions().
 | 
			
		||||
   *   - Plugin name to query for.
 | 
			
		||||
   */
 | 
			
		||||
  public static function providerGetDefinition() {
 | 
			
		||||
    return [
 | 
			
		||||
      ['definition', [], ['plugin_name' => 'definition'], 'plugin_name'],
 | 
			
		||||
      ['definition', ['plugin_name' => 'definition'], [], 'plugin_name'],
 | 
			
		||||
      [NULL, ['plugin_name' => 'definition'], [], 'bad_plugin_name'],
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @covers ::getDefinition
 | 
			
		||||
   * @dataProvider providerGetDefinition
 | 
			
		||||
   */
 | 
			
		||||
  public function testGetDefinition($expected, $cached_definitions, $get_definitions, $plugin_id): void {
 | 
			
		||||
    $trait = $this->getMockBuilder(DiscoveryCachedTraitMockableClass::class)
 | 
			
		||||
      ->onlyMethods(['getDefinitions'])
 | 
			
		||||
      ->getMock();
 | 
			
		||||
    $reflection_definitions = new \ReflectionProperty($trait, 'definitions');
 | 
			
		||||
    // getDefinition() needs the ::$definitions property to be set in one of two
 | 
			
		||||
    // ways: 1) As existing cached data, or 2) as a side-effect of calling
 | 
			
		||||
    // getDefinitions().
 | 
			
		||||
    // If there are no cached definitions, then we have to fake the side-effect
 | 
			
		||||
    // of getDefinitions().
 | 
			
		||||
    if (count($cached_definitions) < 1) {
 | 
			
		||||
      $trait->expects($this->once())
 | 
			
		||||
        ->method('getDefinitions')
 | 
			
		||||
        // Use a callback method, so we can perform the side-effects.
 | 
			
		||||
        ->willReturnCallback(function () use ($reflection_definitions, $trait, $get_definitions) {
 | 
			
		||||
          $reflection_definitions->setValue($trait, $get_definitions);
 | 
			
		||||
          return $get_definitions;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      // Put $cached_definitions into our mocked ::$definitions.
 | 
			
		||||
      $reflection_definitions->setValue($trait, $cached_definitions);
 | 
			
		||||
    }
 | 
			
		||||
    // Call getDefinition(), with $exception_on_invalid always FALSE.
 | 
			
		||||
    $this->assertSame(
 | 
			
		||||
      $expected,
 | 
			
		||||
      $trait->getDefinition($plugin_id, FALSE)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A class using the DiscoveryCachedTrait for mocking purposes.
 | 
			
		||||
 */
 | 
			
		||||
class DiscoveryCachedTraitMockableClass {
 | 
			
		||||
 | 
			
		||||
  use DiscoveryCachedTrait;
 | 
			
		||||
 | 
			
		||||
  public function getDefinitions(): array {
 | 
			
		||||
    return [];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user