diff --git a/core/lib/Drupal/Component/Serialization/Yaml.php b/core/lib/Drupal/Component/Serialization/Yaml.php index 5e104ba..3df8f84 100644 --- a/core/lib/Drupal/Component/Serialization/Yaml.php +++ b/core/lib/Drupal/Component/Serialization/Yaml.php @@ -7,42 +7,34 @@ namespace Drupal\Component\Serialization; -use Drupal\Component\Serialization\Exception\InvalidDataTypeException; -use Symfony\Component\Yaml\Parser; -use Symfony\Component\Yaml\Dumper; - /** - * Default serialization for YAML using the Symfony component. + * YAML serialization implementation. + * + * Proxy implementation that will choose the best library based on availability. */ class Yaml implements SerializationInterface { /** + * The YAML implementation to use. + * + * @var \Drupal\Component\Serialization\SerializationInterface + */ + protected static $serializer; + + /** * {@inheritdoc} */ public static function encode($data) { - try { - $yaml = new Dumper(); - $yaml->setIndentation(2); - return $yaml->dump($data, PHP_INT_MAX, 0, TRUE, FALSE); - } - catch (\Exception $e) { - throw new InvalidDataTypeException($e->getMessage(), $e->getCode(), $e); - } + $serializer = static::getSerializer(); + return $serializer::encode($data); } /** * {@inheritdoc} */ public static function decode($raw) { - try { - $yaml = new Parser(); - // Make sure we have a single trailing newline. A very simple config like - // 'foo: bar' with no newline will fail to parse otherwise. - return $yaml->parse($raw, TRUE, FALSE); - } - catch (\Exception $e) { - throw new InvalidDataTypeException($e->getMessage(), $e->getCode(), $e); - } + $serializer = static::getSerializer(); + return $serializer::decode($raw); } /** @@ -52,4 +44,22 @@ public static function getFileExtension() { return 'yml'; } + /** + * Determines the optimal implementation to use for encoding and parsing YAML. + */ + protected static function getSerializer() { + + if (!isset(static::$serializer)) { + // Use the PECL Yaml extension if it is available. + if (extension_loaded('yaml')) { + static::$serializer = '\Drupal\Component\Serialization\YamlPecl'; + } + else { + // Otherwise, fallback to the Symfony implementation. + static::$serializer = '\Drupal\Component\Serialization\YamlSymfony'; + } + } + return static::$serializer; + } + } diff --git a/core/lib/Drupal/Component/Serialization/YamlPecl.php b/core/lib/Drupal/Component/Serialization/YamlPecl.php new file mode 100644 index 0000000..0fd833c --- /dev/null +++ b/core/lib/Drupal/Component/Serialization/YamlPecl.php @@ -0,0 +1,99 @@ + '\Drupal\Component\Serialization\YamlPecl::applyBooleanCallbacks', + ]); + restore_error_handler(); + return $data; + } + + /** + * Temporary error handler for YamlPecl::decode(). + */ + public static function errorHandler($severity, $message, $file, $line) { + restore_error_handler(); + throw new InvalidDataTypeException($message, $severity); + } + + /** + * {@inheritdoc} + */ + public static function getFileExtension() { + return 'yml'; + } + + /** + * Applies callbacks after parsing to ignore 1.1 style booleans. + * + * @param mixed $value + * Value from YAML file. + * @param string $tag + * Tag that triggered the callback. + * @param int $flags + * Scalar entity style flags. + * + * @return string|bool + * FALSE, false, TRUE and true are returned as booleans, everything else is + * returned as a string. + */ + public static function applyBooleanCallbacks($value, $tag, $flags) { + // YAML 1.1 spec dictates that 'Y', 'N', 'y' and 'n' are booleans, we want + // the 1.2 behaviour so we only case 'false', 'FALSE', 'true' OR 'TRUE' as + // booleans. + if (!in_array(strtolower($value), ['false', 'true'], TRUE)) { + return $value; + } + $map = [ + 'false' => FALSE, + 'true' => TRUE, + ]; + return $map[strtolower($value)]; + } + +} diff --git a/core/lib/Drupal/Component/Serialization/Yaml.php b/core/lib/Drupal/Component/Serialization/YamlSymfony.php similarity index 91% copy from core/lib/Drupal/Component/Serialization/Yaml.php copy to core/lib/Drupal/Component/Serialization/YamlSymfony.php index 5e104ba..4ca5e25 100644 --- a/core/lib/Drupal/Component/Serialization/Yaml.php +++ b/core/lib/Drupal/Component/Serialization/YamlSymfony.php @@ -2,7 +2,7 @@ /** * @file - * Contains \Drupal\Component\Serialization\Yaml. + * Contains \Drupal\Component\Serialization\YamlSymfony. */ namespace Drupal\Component\Serialization; @@ -14,7 +14,7 @@ /** * Default serialization for YAML using the Symfony component. */ -class Yaml implements SerializationInterface { +class YamlSymfony implements SerializationInterface { /** * {@inheritdoc} diff --git a/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php b/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php index e6a4376..33ef480 100644 --- a/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php +++ b/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php @@ -11,9 +11,9 @@ use Drupal\Core\Asset\Exception\InvalidLibraryFileException; use Drupal\Core\Asset\Exception\LibraryDefinitionMissingLicenseException; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Serialization\Yaml; use Drupal\Core\Theme\ThemeManagerInterface; use Drupal\Component\Serialization\Exception\InvalidDataTypeException; -use Drupal\Component\Serialization\Yaml; use Drupal\Component\Utility\NestedArray; /** diff --git a/core/lib/Drupal/Core/Config/ConfigManager.php b/core/lib/Drupal/Core/Config/ConfigManager.php index fb8f658..37675bc 100644 --- a/core/lib/Drupal/Core/Config/ConfigManager.php +++ b/core/lib/Drupal/Core/Config/ConfigManager.php @@ -8,7 +8,7 @@ namespace Drupal\Core\Config; use Drupal\Component\Diff\Diff; -use Drupal\Component\Serialization\Yaml; +use Drupal\Core\Serialization\Yaml; use Drupal\Core\Config\Entity\ConfigDependencyManager; use Drupal\Core\Config\Entity\ConfigEntityInterface; use Drupal\Core\Config\Entity\ConfigEntityTypeInterface; diff --git a/core/lib/Drupal/Core/Config/FileStorage.php b/core/lib/Drupal/Core/Config/FileStorage.php index aaac182..d486551 100644 --- a/core/lib/Drupal/Core/Config/FileStorage.php +++ b/core/lib/Drupal/Core/Config/FileStorage.php @@ -7,7 +7,7 @@ namespace Drupal\Core\Config; -use Drupal\Component\Serialization\Yaml; +use Drupal\Core\Serialization\Yaml; use Drupal\Component\Serialization\Exception\InvalidDataTypeException; /** diff --git a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php index 4c7fea9..a8dd98f 100644 --- a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php +++ b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php @@ -7,8 +7,8 @@ namespace Drupal\Core\DependencyInjection; +use Drupal\Core\Serialization\Yaml; use Drupal\Component\FileCache\FileCacheFactory; -use Drupal\Component\Serialization\Yaml; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; diff --git a/core/lib/Drupal/Core/Discovery/YamlDiscovery.php b/core/lib/Drupal/Core/Discovery/YamlDiscovery.php new file mode 100644 index 0000000..bac6219 --- /dev/null +++ b/core/lib/Drupal/Core/Discovery/YamlDiscovery.php @@ -0,0 +1,30 @@ +findFiles() as $provider => $file) { + $all[$provider] = Yaml::decode(file_get_contents($file)); + } + + return $all; + } + +} diff --git a/core/lib/Drupal/Core/Extension/InfoParserDynamic.php b/core/lib/Drupal/Core/Extension/InfoParserDynamic.php index cf25594..3d648b2 100644 --- a/core/lib/Drupal/Core/Extension/InfoParserDynamic.php +++ b/core/lib/Drupal/Core/Extension/InfoParserDynamic.php @@ -7,7 +7,7 @@ namespace Drupal\Core\Extension; -use Drupal\Component\Serialization\Yaml; +use Drupal\Core\Serialization\Yaml; use Drupal\Component\Serialization\Exception\InvalidDataTypeException; use Drupal\Component\Utility\SafeMarkup; diff --git a/core/lib/Drupal/Core/Extension/ModuleInstaller.php b/core/lib/Drupal/Core/Extension/ModuleInstaller.php index a8124d5..7b7b53a 100644 --- a/core/lib/Drupal/Core/Extension/ModuleInstaller.php +++ b/core/lib/Drupal/Core/Extension/ModuleInstaller.php @@ -7,7 +7,7 @@ namespace Drupal\Core\Extension; -use Drupal\Component\Serialization\Yaml; +use Drupal\Core\Serialization\Yaml; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\DrupalKernelInterface; diff --git a/core/lib/Drupal/Core/Plugin/Discovery/YamlDiscovery.php b/core/lib/Drupal/Core/Plugin/Discovery/YamlDiscovery.php index 0d519f4..1527989 100644 --- a/core/lib/Drupal/Core/Plugin/Discovery/YamlDiscovery.php +++ b/core/lib/Drupal/Core/Plugin/Discovery/YamlDiscovery.php @@ -8,7 +8,7 @@ namespace Drupal\Core\Plugin\Discovery; use Drupal\Component\Plugin\Discovery\DiscoveryInterface; -use Drupal\Component\Discovery\YamlDiscovery as ComponentYamlDiscovery; +use Drupal\Core\Discovery\YamlDiscovery as CoreYamlDiscovery; use Drupal\Component\Plugin\Discovery\DiscoveryTrait; use Drupal\Core\StringTranslation\TranslationWrapper; @@ -53,7 +53,7 @@ class YamlDiscovery implements DiscoveryInterface { * An array of directories to scan. */ function __construct($name, array $directories) { - $this->discovery = new ComponentYamlDiscovery($name, $directories); + $this->discovery = new CoreYamlDiscovery($name, $directories); } /** diff --git a/core/lib/Drupal/Core/Serialization/Yaml.php b/core/lib/Drupal/Core/Serialization/Yaml.php new file mode 100644 index 0000000..03effff --- /dev/null +++ b/core/lib/Drupal/Core/Serialization/Yaml.php @@ -0,0 +1,33 @@ +infoParser = new InfoParser(); + } + + /** + * Tests the functionality of the infoParser object. + */ + public function testInfoParser() { + $info = $this->infoParser->parse('core/modules/system/tests/fixtures/does_not_exist.info.txt'); + $this->assertTrue(empty($info), 'Non existing info.yml returns empty array.'); + + // Test that invalid YAML throws an exception and that message contains the + // filename that caused it. + $filename = 'core/modules/system/tests/fixtures/broken.info.txt'; + try { + $this->infoParser->parse($filename); + $this->fail('Expected InfoParserException not thrown when reading broken.info.txt'); + } + catch (InfoParserException $e) { + $this->assertTrue(strpos($e->getMessage(), $filename) !== FALSE, 'Exception message contains info.yml filename.'); + } + + // Tests that missing required keys are detected. + $filename = 'core/modules/system/tests/fixtures/missing_keys.info.txt'; + try { + $this->infoParser->parse($filename); + $this->fail('Expected InfoParserException not thrown when reading missing_keys.info.txt'); + } + catch (InfoParserException $e) { + $expected_message = "Missing required keys (type, core, name) in $filename."; + $this->assertEqual($e->getMessage(), $expected_message); + } + + // Tests that a single missing required key is detected. + $filename = 'core/modules/system/tests/fixtures/missing_key.info.txt'; + try { + $this->infoParser->parse($filename); + $this->fail('Expected InfoParserException not thrown when reading missing_key.info.txt'); + } + catch (InfoParserException $e) { + $expected_message = "Missing required keys (type) in $filename."; + $this->assertEqual($e->getMessage(), $expected_message); + } + + $info_values = $this->infoParser->parse('core/modules/system/tests/fixtures/common_test.info.txt'); + $this->assertEqual($info_values['simple_string'], 'A simple string', 'Simple string value was parsed correctly.', 'System'); + $this->assertEqual($info_values['version'], \Drupal::VERSION, 'Constant value was parsed correctly.', 'System'); + $this->assertEqual($info_values['double_colon'], 'dummyClassName::foo', 'Value containing double-colon was parsed correctly.', 'System'); + } + +} diff --git a/core/modules/system/tests/fixtures/common_test.info.txt b/core/modules/system/tests/fixtures/common_test.info.txt new file mode 100644 index 0000000..ff535b1 --- /dev/null +++ b/core/modules/system/tests/fixtures/common_test.info.txt @@ -0,0 +1,7 @@ +core: 8.x +name: common_test +type: module +description: 'testing info file parsing' +simple_string: 'A simple string' +version: "VERSION" +double_colon: dummyClassName::foo diff --git a/core/modules/system/tests/modules/theme_test/theme_test.services.yml b/core/modules/system/tests/modules/theme_test/theme_test.services.yml index 482c219..66d5607 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.services.yml +++ b/core/modules/system/tests/modules/theme_test/theme_test.services.yml @@ -1,7 +1,7 @@ services: theme_test.subscriber: class: Drupal\theme_test\EventSubscriber\ThemeTestSubscriber - arguments: [@current_route_match, @renderer] + arguments: ["@current_route_match", "@renderer"] tags: - { name: event_subscriber } diff --git a/core/modules/taxonomy/migration_templates/d6_vocabulary_field_instance.yml b/core/modules/taxonomy/migration_templates/d6_vocabulary_field_instance.yml index 3f6ef78..5ed647a 100644 --- a/core/modules/taxonomy/migration_templates/d6_vocabulary_field_instance.yml +++ b/core/modules/taxonomy/migration_templates/d6_vocabulary_field_instance.yml @@ -20,7 +20,7 @@ process: plugin: skip_on_empty method: row 'settings/handler': 'constants/selection_handler' - 'settings/handler_settings/target_bundles/0': @field_name + 'settings/handler_settings/target_bundles/0': '@field_name' 'settings/handler_settings/auto_create': 'constants/auto_create' destination: plugin: entity:field_config diff --git a/core/modules/user/migration_templates/user_picture_entity_display.yml b/core/modules/user/migration_templates/user_picture_entity_display.yml index 78c0ae0..3f404d6 100644 --- a/core/modules/user/migration_templates/user_picture_entity_display.yml +++ b/core/modules/user/migration_templates/user_picture_entity_display.yml @@ -23,7 +23,7 @@ process: field_name: 'constants/name' type: 'constants/type' options: 'constants/options' - 'options/type': @type + 'options/type': '@type' destination: plugin: component_entity_display migration_dependencies: diff --git a/core/modules/user/migration_templates/user_picture_entity_form_display.yml b/core/modules/user/migration_templates/user_picture_entity_form_display.yml index 60d782f..4d0f8bc 100644 --- a/core/modules/user/migration_templates/user_picture_entity_form_display.yml +++ b/core/modules/user/migration_templates/user_picture_entity_form_display.yml @@ -22,7 +22,7 @@ process: form_mode: 'constants/form_mode' type: 'constants/type' options: 'constants/options' - 'options/type': @type + 'options/type': '@type' destination: plugin: component_entity_form_display migration_dependencies: diff --git a/core/modules/user/migration_templates/user_profile_entity_display.yml b/core/modules/user/migration_templates/user_profile_entity_display.yml index 76553a1..84e593e 100644 --- a/core/modules/user/migration_templates/user_profile_entity_display.yml +++ b/core/modules/user/migration_templates/user_profile_entity_display.yml @@ -29,7 +29,7 @@ process: textarea: text_default url: link_default options: 'constants/options' - 'options/type': @type + 'options/type': '@type' hidden: plugin: static_map source: visibility diff --git a/core/modules/user/migration_templates/user_profile_entity_form_display.yml b/core/modules/user/migration_templates/user_profile_entity_form_display.yml index c75c2c8..1aab3b7 100644 --- a/core/modules/user/migration_templates/user_profile_entity_form_display.yml +++ b/core/modules/user/migration_templates/user_profile_entity_form_display.yml @@ -27,11 +27,11 @@ process: textarea: text_textarea url: link_default options: 'constants/options' - 'options/type': @type + 'options/type': '@type' 'options/settings': plugin: field_instance_widget_settings source: - - @type + - '@type' - 'constants/empty' # we don't have any settings. 'options/settings/display_label': # Single on/off checkboxes need to have display_label = true otherwise their label doesn't show. plugin: static_map diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_group_rows.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_group_rows.yml index 2c34841..294e2fd 100644 --- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_group_rows.yml +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_group_rows.yml @@ -7,7 +7,6 @@ dependencies: - user config: - field.storage.node.field_views_testing_group_rows - module: id: test_group_rows label: test_group_rows module: views diff --git a/core/profiles/standard/config/install/editor.editor.full_html.yml b/core/profiles/standard/config/install/editor.editor.full_html.yml index 80da28c..34800d1 100644 --- a/core/profiles/standard/config/install/editor.editor.full_html.yml +++ b/core/profiles/standard/config/install/editor.editor.full_html.yml @@ -12,7 +12,7 @@ settings: - Strike - Superscript - Subscript - - - + - '-' - RemoveFormat - name: Linking diff --git a/core/tests/Drupal/Tests/Component/Serialization/YamlPeclTest.php b/core/tests/Drupal/Tests/Component/Serialization/YamlPeclTest.php new file mode 100644 index 0000000..5d94c91 --- /dev/null +++ b/core/tests/Drupal/Tests/Component/Serialization/YamlPeclTest.php @@ -0,0 +1,87 @@ +assertEquals($data, YamlPecl::decode(YamlPecl::encode($data))); + } + + /** + * Tests decoding YAML node anchors. + * + * @covers ::decode + * @dataProvider providerDecodeTests + */ + public function testDecode($string, $data) { + $this->assertEquals($data, YamlPecl::decode($string)); + } + + /** + * Tests our encode settings. + * + * @covers ::encode + */ + public function testEncode() { + $this->assertEquals('--- +foo: + bar: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sapien ex, venenatis vitae nisi eu, posuere luctus dolor. Nullam convallis +... +', YamlPecl::encode(['foo' => ['bar' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sapien ex, venenatis vitae nisi eu, posuere luctus dolor. Nullam convallis']])); + } + + /** + * Test yaml boolean callback. + * + * @covers ::applyBooleanCallbacks + * @dataProvider providerBoolTest + * @param string $string + * String value for the yaml boolean. + * @param string|bool $expected + * The expected return + */ + public function testApplyBooleanCallbacks($string, $expected) { + $this->assertEquals($expected, YamlPecl::applyBooleanCallbacks($string, 'bool', NULL)); + } + + /** + * @covers ::getFileExtension + */ + public function testGetFileExtension() { + $this->assertEquals('yml', YamlPecl::getFileExtension()); + } + + /** + * Invalid yaml throws exception. + * + * @covers ::errorHandler + * @expectedException \Drupal\Component\Serialization\Exception\InvalidDataTypeException + */ + public function testError() { + YamlPecl::decode('foo: [ads'); + } + +} diff --git a/core/tests/Drupal/Tests/Component/Serialization/YamlSymfonyTest.php b/core/tests/Drupal/Tests/Component/Serialization/YamlSymfonyTest.php new file mode 100644 index 0000000..cafe3be --- /dev/null +++ b/core/tests/Drupal/Tests/Component/Serialization/YamlSymfonyTest.php @@ -0,0 +1,71 @@ +assertEquals($data, YamlSymfony::decode(YamlSymfony::encode($data))); + } + + /** + * Tests decoding YAML node anchors. + * + * @covers ::decode + * @dataProvider providerDecodeTests + */ + public function testDecode($string, $data) { + $this->assertEquals($data, YamlSymfony::decode($string)); + } + + /** + * Tests our encode settings. + * + * @covers ::encode + */ + public function testEncode() { + $this->assertEquals('foo: + bar: \'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sapien ex, venenatis vitae nisi eu, posuere luctus dolor. Nullam convallis\' +', YamlSymfony::encode(['foo' => ['bar' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sapien ex, venenatis vitae nisi eu, posuere luctus dolor. Nullam convallis']])); + } + + /** + * @covers ::getFileExtension + */ + public function testGetFileExtension() { + $this->assertEquals('yml', YamlSymfony::getFileExtension()); + } + + /** + * Invalid yaml throws exception. + * + * @covers ::decode + * @expectedException \Drupal\Component\Serialization\Exception\InvalidDataTypeException + */ + public function testError() { + YamlSymfony::decode('foo: [ads'); + } + +} diff --git a/core/tests/Drupal/Tests/Component/Serialization/YamlTest.php b/core/tests/Drupal/Tests/Component/Serialization/YamlTest.php index 94269e6..972fa31 100644 --- a/core/tests/Drupal/Tests/Component/Serialization/YamlTest.php +++ b/core/tests/Drupal/Tests/Component/Serialization/YamlTest.php @@ -7,7 +7,11 @@ namespace Drupal\Tests\Component\Serialization; +use Drupal\Component\Serialization\Exception\InvalidDataTypeException; +use Drupal\Component\Serialization\SerializationInterface; use Drupal\Component\Serialization\Yaml; +use Drupal\Component\Serialization\YamlPecl; +use Drupal\Component\Serialization\YamlSymfony; use Drupal\Tests\UnitTestCase; /** @@ -20,44 +24,118 @@ class YamlTest extends UnitTestCase { * @covers ::decode */ public function testDecode() { - // Test that files without line break endings are properly interpreted. - $yaml = 'foo: bar'; - $expected = array( - 'foo' => 'bar', - ); - $this->assertSame($expected, Yaml::decode($yaml)); - $yaml .= "\n"; - $this->assertSame($expected, Yaml::decode($yaml)); - $yaml .= "\n"; - $this->assertSame($expected, Yaml::decode($yaml)); - - $yaml = "{}\n"; - $expected = array(); - $this->assertSame($expected, Yaml::decode($yaml)); - - $yaml = ''; - $this->assertNULL(Yaml::decode($yaml)); - $yaml .= "\n"; - $this->assertNULL(Yaml::decode($yaml)); - $yaml .= "\n"; - $this->assertNULL(Yaml::decode($yaml)); + $stub = $this->getMockBuilder('\stdClass') + ->setMethods(['encode', 'decode', 'getFileExtension']) + ->getMock(); + $stub + ->expects($this->once()) + ->method('decode'); + YamlParserProxy::setMock($stub); + YamlStub::decode('test'); } /** * @covers ::encode */ public function testEncode() { - $decoded = array( - 'foo' => 'bar', - ); - $this->assertSame('foo: bar' . "\n", Yaml::encode($decoded)); + $stub = $this->getMockBuilder('\stdClass') + ->setMethods(['encode', 'decode', 'getFileExtension']) + ->getMock(); + $stub + ->expects($this->once()) + ->method('encode'); + YamlParserProxy::setMock($stub); + YamlStub::encode([]); } /** * @covers ::getFileExtension */ public function testGetFileExtension() { - $this->assertEquals('yml', Yaml::getFileExtension()); + $stub = $this->getMockBuilder('\stdClass') + ->setMethods(['encode', 'decode', 'getFileExtension']) + ->getMock(); + $stub + ->expects($this->never()) + ->method('getFileExtension'); + YamlParserProxy::setMock($stub); + $this->assertEquals('yml', YamlStub::getFileExtension()); + } + + /** + * Tests all YAML files are decoded in the same way with both Symfony and PECL. + * + * This tests is a little bit slow but it tests that we don't have any bugs + * in our YAML that might not be decoded correctly in on of our + * implementations. + * + * @todo This should exist as an integration test not part of our unit tests. + * + * @requires extension yaml + * @dataProvider providerYamlFilesInCore + */ + public function testYamlFiles($file) { + $data = file_get_contents($file); + try { + $this->assertEquals(YamlSymfony::decode($data), YamlPecl::decode($data), $file); + } + catch (InvalidDataTypeException $e) { + // Provide file context to the failure so the exception message is useful. + $this->fail("Exception thrown parsing $file:\n" . $e->getMessage()); + } + } + + /** + * Data provider that lists all YAML files in core. + */ + public function providerYamlFilesInCore() { + $files = []; + $dirs = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(__DIR__ . '/../../../../../', \RecursiveDirectoryIterator::FOLLOW_SYMLINKS)); + foreach ($dirs as $dir) { + $pathname = $dir->getPathname(); + // Exclude vendor. + if ($dir->getExtension() == 'yml' && strpos($pathname, '/../../../../../vendor') === FALSE) { + if (strpos($dir->getRealPath(), 'invalid_file') !== FALSE) { + // There are some intentionally invalid files provided for testing + // library API behaviours, ignore them. + continue; + } + $files[] = array($dir->getRealPath()); + } + } + return $files; + } +} + +class YamlStub extends Yaml { + + public static function getSerializer() { + return '\Drupal\Tests\Component\Serialization\YamlParserProxy'; + } + +} + +class YamlParserProxy implements SerializationInterface { + + /** + * @var \Drupal\Component\Serialization\SerializationInterface + */ + static $mock; + + public static function setMock($mock) { + static::$mock = $mock; + } + + public static function encode($data) { + return static::$mock->encode($data); + } + + public static function decode($raw) { + return static::$mock->decode($raw); + } + + public static function getFileExtension() { + return static::$mock->getFileExtension(); } } diff --git a/core/tests/Drupal/Tests/Component/Serialization/YamlTestBase.php b/core/tests/Drupal/Tests/Component/Serialization/YamlTestBase.php new file mode 100644 index 0000000..8a4614b --- /dev/null +++ b/core/tests/Drupal/Tests/Component/Serialization/YamlTestBase.php @@ -0,0 +1,104 @@ + 'bar', + 'id' => 'schnitzel', + 'ponies' => ['nope', 'thanks'], + 'how' => [ + 'about' => 'if', + 'i' => 'ask', + 'nicely', + ], + 'the' => [ + 'answer' => [ + 'still' => 'would', + 'be' => 'Y', + ], + ], + 'how_many_times' => 123, + 'should_i_ask' => FALSE, + 1, + FALSE, + [1, FALSE], + [10], + [0 => '123456'], + ], + [NULL] + ]; + } + + /** + * Some data that should be able to be de-serialized. + */ + public function providerDecodeTests() { + $data = [ + // NULL files. + ['', NULL], + ["\n", NULL], + ["---\n...\n", NULL], + + // Node anchors. + [ + " +jquery.ui: + version: &jquery_ui 1.10.2 + +jquery.ui.accordion: + version: *jquery_ui +", + [ + 'jquery.ui' => [ + 'version' => '1.10.2', + ], + 'jquery.ui.accordion' => [ + 'version' => '1.10.2', + ], + ], + ], + ]; + + // 1.2 Bool values. + foreach ($this->providerBoolTest() as $test) { + $data[] = ['bool: ' . $test[0], ['bool' => $test[1]]]; + } + $data = array_merge($data, $this->providerBoolTest()); + + return $data; + } + + /** + * Test different boolean serialization and de-serialization. + */ + public function providerBoolTest() { + return [ + ['true', TRUE], + ['TRUE', TRUE], + ['True', TRUE], + ['y', 'y'], + ['Y', 'Y'], + ['false', FALSE], + ['FALSE', FALSE], + ['False', FALSE], + ['n', 'n'], + ['N', 'N'], + ]; + } + +} diff --git a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php index 9865cb2..e0135ef 100644 --- a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php +++ b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php @@ -216,34 +216,6 @@ public function testVersion() { $this->assertEquals(\Drupal::VERSION, $libraries['core-versioned']['js'][0]['version']); } - /** - * Tests the version property with ISO dates. - * - * We want to make sure that versions defined in the YAML file are the same - * versions that are parsed. - * - * For example, ISO dates are converted into UNIX time by the YAML parser. - * - * @covers ::buildByExtension - */ - public function testNonStringVersion() { - $this->moduleHandler->expects($this->atLeastOnce()) - ->method('moduleExists') - ->with('versions') - ->will($this->returnValue(TRUE)); - - $path = __DIR__ . '/library_test_files'; - $path = substr($path, strlen($this->root) + 1); - $this->libraryDiscoveryParser->setPaths('module', 'versions', $path); - - $libraries = $this->libraryDiscoveryParser->buildByExtension('versions'); - - // As an example, we defined an ISO date in the YAML file and the YAML - // parser converts it into a UNIX timestamp. - $this->assertNotEquals('2014-12-13', $libraries['invalid-version']['version']); - // An example of an ISO date as a string which parses correctly. - $this->assertEquals('2014-12-13', $libraries['valid-version']['version']); - } /** * Tests that the version property of external libraries is handled. diff --git a/core/tests/Drupal/Tests/Core/Asset/library_test_files/versions.libraries.yml b/core/tests/Drupal/Tests/Core/Asset/library_test_files/versions.libraries.yml index bee49f5..7310a09 100644 --- a/core/tests/Drupal/Tests/Core/Asset/library_test_files/versions.libraries.yml +++ b/core/tests/Drupal/Tests/Core/Asset/library_test_files/versions.libraries.yml @@ -20,13 +20,3 @@ core-versioned: core-versioned.css: {} js: core-versioned.js: {} - -invalid-version: - version: 2014-12-13 - js: - versioned.js: {} - -valid-version: - version: "2014-12-13" - js: - versioned.js: {} diff --git a/core/tests/Drupal/Tests/Core/Extension/InfoParserUnitTest.php b/core/tests/Drupal/Tests/Core/Extension/InfoParserUnitTest.php index 71948e1..876d3b0 100644 --- a/core/tests/Drupal/Tests/Core/Extension/InfoParserUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/InfoParserUnitTest.php @@ -152,7 +152,7 @@ public function testInfoParserCommonInfo() { description: 'testing info file parsing' simple_string: 'A simple string' version: "VERSION" -double_colon: dummyClassName:: +double_colon: dummyClassName::method COMMONTEST; vfsStream::setup('modules'); @@ -164,7 +164,7 @@ public function testInfoParserCommonInfo() { $info_values = $this->infoParser->parse(vfsStream::url('modules/fixtures/common_test.info.txt')); $this->assertEquals($info_values['simple_string'], 'A simple string', 'Simple string value was parsed correctly.'); $this->assertEquals($info_values['version'], \Drupal::VERSION, 'Constant value was parsed correctly.'); - $this->assertEquals($info_values['double_colon'], 'dummyClassName::', 'Value containing double-colon was parsed correctly.'); + $this->assertEquals($info_values['double_colon'], 'dummyClassName::method', 'Value containing double-colon was parsed correctly.'); } }