From de214f3e11d35259282a35370a4284e70070b8c7 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Sun, 29 Oct 2023 15:31:39 +0100 Subject: [PATCH] Provide better reader matchers --- psalm.xml | 5 + src/Xml/Dom/Loader/xml_file_loader.php | 2 +- src/Xml/Dom/Loader/xml_string_loader.php | 2 +- src/Xml/Reader/Matcher/any.php | 25 ++++ .../Reader/Matcher/attribute_local_name.php | 23 ++++ .../Reader/Matcher/attribute_local_value.php | 23 ++++ src/Xml/Reader/Matcher/attribute_name.php | 23 ++++ src/Xml/Reader/Matcher/attribute_value.php | 23 ++++ src/Xml/Reader/Matcher/document_element.php | 18 +++ src/Xml/Reader/Matcher/element_local_name.php | 18 +++ src/Xml/Reader/Matcher/element_name.php | 18 +++ src/Xml/Reader/Matcher/element_position.php | 18 +++ .../Reader/Matcher/namespaced_attribute.php | 23 ++++ .../Matcher/namespaced_attribute_value.php | 26 ++++ src/Xml/Reader/Matcher/namespaced_element.php | 20 +++ src/Xml/Reader/Matcher/node_attribute.php | 1 + src/Xml/Reader/Matcher/node_name.php | 1 + src/Xml/Reader/Matcher/sequence.php | 41 ++++++ src/bootstrap.php | 13 ++ .../Reader/Matcher/AbstractMatcherTest.php | 42 +++++++ tests/Xml/Reader/Matcher/AllTest.php | 88 +++++++++---- tests/Xml/Reader/Matcher/AnyTest.php | 84 +++++++++++++ .../Reader/Matcher/AttributeLocalNameTest.php | 68 ++++++++++ .../Matcher/AttributeLocalValueTest.php | 73 +++++++++++ .../Xml/Reader/Matcher/AttributeNameTest.php | 67 ++++++++++ .../Xml/Reader/Matcher/AttributeValueTest.php | 73 +++++++++++ .../Reader/Matcher/DocumentElementTest.php | 52 ++++++++ .../Reader/Matcher/ElementLocalNameTest.php | 66 ++++++++++ tests/Xml/Reader/Matcher/ElementNameTest.php | 66 ++++++++++ .../Reader/Matcher/ElementPositionTest.php | 49 ++++++++ .../Matcher/NamespacedAttributeTest.php | 59 +++++++++ .../Matcher/NamespacedAttributeValueTest.php | 64 ++++++++++ .../Matcher/NamespacedElementNameTest.php | 56 +++++++++ .../Xml/Reader/Matcher/NodeAttributeTest.php | 75 +++++++---- tests/Xml/Reader/Matcher/NodeNameTest.php | 64 +++++++--- tests/Xml/Reader/Matcher/SequenceTest.php | 119 ++++++++++++++++++ 36 files changed, 1424 insertions(+), 64 deletions(-) create mode 100644 src/Xml/Reader/Matcher/any.php create mode 100644 src/Xml/Reader/Matcher/attribute_local_name.php create mode 100644 src/Xml/Reader/Matcher/attribute_local_value.php create mode 100644 src/Xml/Reader/Matcher/attribute_name.php create mode 100644 src/Xml/Reader/Matcher/attribute_value.php create mode 100644 src/Xml/Reader/Matcher/document_element.php create mode 100644 src/Xml/Reader/Matcher/element_local_name.php create mode 100644 src/Xml/Reader/Matcher/element_name.php create mode 100644 src/Xml/Reader/Matcher/element_position.php create mode 100644 src/Xml/Reader/Matcher/namespaced_attribute.php create mode 100644 src/Xml/Reader/Matcher/namespaced_attribute_value.php create mode 100644 src/Xml/Reader/Matcher/namespaced_element.php create mode 100644 src/Xml/Reader/Matcher/sequence.php create mode 100644 tests/Xml/Reader/Matcher/AbstractMatcherTest.php create mode 100644 tests/Xml/Reader/Matcher/AnyTest.php create mode 100644 tests/Xml/Reader/Matcher/AttributeLocalNameTest.php create mode 100644 tests/Xml/Reader/Matcher/AttributeLocalValueTest.php create mode 100644 tests/Xml/Reader/Matcher/AttributeNameTest.php create mode 100644 tests/Xml/Reader/Matcher/AttributeValueTest.php create mode 100644 tests/Xml/Reader/Matcher/DocumentElementTest.php create mode 100644 tests/Xml/Reader/Matcher/ElementLocalNameTest.php create mode 100644 tests/Xml/Reader/Matcher/ElementNameTest.php create mode 100644 tests/Xml/Reader/Matcher/ElementPositionTest.php create mode 100644 tests/Xml/Reader/Matcher/NamespacedAttributeTest.php create mode 100644 tests/Xml/Reader/Matcher/NamespacedAttributeValueTest.php create mode 100644 tests/Xml/Reader/Matcher/NamespacedElementNameTest.php create mode 100644 tests/Xml/Reader/Matcher/SequenceTest.php diff --git a/psalm.xml b/psalm.xml index 5a7bd22c..95604c82 100644 --- a/psalm.xml +++ b/psalm.xml @@ -32,6 +32,11 @@ + + + + + diff --git a/src/Xml/Dom/Loader/xml_file_loader.php b/src/Xml/Dom/Loader/xml_file_loader.php index 28e00adf..06d560ed 100644 --- a/src/Xml/Dom/Loader/xml_file_loader.php +++ b/src/Xml/Dom/Loader/xml_file_loader.php @@ -18,7 +18,7 @@ function xml_file_loader(string $file, int $options = 0): Closure load( static function () use ($document, $file, $options): bool { Assert::fileExists($file); - return (bool) $document->load($file, $options); + return $document->load($file, $options); } ); }; diff --git a/src/Xml/Dom/Loader/xml_string_loader.php b/src/Xml/Dom/Loader/xml_string_loader.php index 1dfa2fc6..333a3354 100644 --- a/src/Xml/Dom/Loader/xml_string_loader.php +++ b/src/Xml/Dom/Loader/xml_string_loader.php @@ -15,6 +15,6 @@ function xml_string_loader(string $xml, int $options = 0): Closure { return static function (DOMDocument $document) use ($xml, $options): void { - load(static fn (): bool => (bool) $document->loadXML($xml, $options)); + load(static fn (): bool => $document->loadXML($xml, $options)); }; } diff --git a/src/Xml/Reader/Matcher/any.php b/src/Xml/Reader/Matcher/any.php new file mode 100644 index 00000000..f88aac76 --- /dev/null +++ b/src/Xml/Reader/Matcher/any.php @@ -0,0 +1,25 @@ + $matchers + * + * @return \Closure(NodeSequence): bool + */ +function any(callable ... $matchers): Closure +{ + return static fn (NodeSequence $sequence): bool => Iter\any( + $matchers, + /** + * @param callable(NodeSequence): bool $matcher + */ + static fn (callable $matcher): bool => $matcher($sequence) + ); +} diff --git a/src/Xml/Reader/Matcher/attribute_local_name.php b/src/Xml/Reader/Matcher/attribute_local_name.php new file mode 100644 index 00000000..ab1eb3a3 --- /dev/null +++ b/src/Xml/Reader/Matcher/attribute_local_name.php @@ -0,0 +1,23 @@ +current()->attributes(), + static fn (AttributeNode $attribute): bool => $attribute->localName() === $localName + ); + }; +} diff --git a/src/Xml/Reader/Matcher/attribute_local_value.php b/src/Xml/Reader/Matcher/attribute_local_value.php new file mode 100644 index 00000000..2a038c5c --- /dev/null +++ b/src/Xml/Reader/Matcher/attribute_local_value.php @@ -0,0 +1,23 @@ +current()->attributes(), + static fn (AttributeNode $attribute): bool => $attribute->localName() === $localName && $attribute->value() === $value + ); + }; +} diff --git a/src/Xml/Reader/Matcher/attribute_name.php b/src/Xml/Reader/Matcher/attribute_name.php new file mode 100644 index 00000000..813a2696 --- /dev/null +++ b/src/Xml/Reader/Matcher/attribute_name.php @@ -0,0 +1,23 @@ +current()->attributes(), + static fn (AttributeNode $attribute): bool => $attribute->name() === $name + ); + }; +} diff --git a/src/Xml/Reader/Matcher/attribute_value.php b/src/Xml/Reader/Matcher/attribute_value.php new file mode 100644 index 00000000..ef00ba41 --- /dev/null +++ b/src/Xml/Reader/Matcher/attribute_value.php @@ -0,0 +1,23 @@ +current()->attributes(), + static fn (AttributeNode $attribute): bool => $attribute->name() === $name && $attribute->value() === $value + ); + }; +} diff --git a/src/Xml/Reader/Matcher/document_element.php b/src/Xml/Reader/Matcher/document_element.php new file mode 100644 index 00000000..0b81a8e5 --- /dev/null +++ b/src/Xml/Reader/Matcher/document_element.php @@ -0,0 +1,18 @@ +parent(); + }; +} diff --git a/src/Xml/Reader/Matcher/element_local_name.php b/src/Xml/Reader/Matcher/element_local_name.php new file mode 100644 index 00000000..a72d7bc5 --- /dev/null +++ b/src/Xml/Reader/Matcher/element_local_name.php @@ -0,0 +1,18 @@ +current()->localName() === $localName; + }; +} diff --git a/src/Xml/Reader/Matcher/element_name.php b/src/Xml/Reader/Matcher/element_name.php new file mode 100644 index 00000000..05b345b4 --- /dev/null +++ b/src/Xml/Reader/Matcher/element_name.php @@ -0,0 +1,18 @@ +current()->name() === $name; + }; +} diff --git a/src/Xml/Reader/Matcher/element_position.php b/src/Xml/Reader/Matcher/element_position.php new file mode 100644 index 00000000..6dd0309f --- /dev/null +++ b/src/Xml/Reader/Matcher/element_position.php @@ -0,0 +1,18 @@ +current()->position() === $position; + }; +} diff --git a/src/Xml/Reader/Matcher/namespaced_attribute.php b/src/Xml/Reader/Matcher/namespaced_attribute.php new file mode 100644 index 00000000..ceb3103a --- /dev/null +++ b/src/Xml/Reader/Matcher/namespaced_attribute.php @@ -0,0 +1,23 @@ +current()->attributes(), + static fn (AttributeNode $attribute): bool => $attribute->localName() === $localName && $attribute->namespace() === $namespace + ); + }; +} diff --git a/src/Xml/Reader/Matcher/namespaced_attribute_value.php b/src/Xml/Reader/Matcher/namespaced_attribute_value.php new file mode 100644 index 00000000..eeab1f43 --- /dev/null +++ b/src/Xml/Reader/Matcher/namespaced_attribute_value.php @@ -0,0 +1,26 @@ +current()->attributes(), + static fn (AttributeNode $attribute): bool => + $attribute->localName() === $localName + && $attribute->namespace() === $namespace + && $attribute->value() === $value + ); + }; +} diff --git a/src/Xml/Reader/Matcher/namespaced_element.php b/src/Xml/Reader/Matcher/namespaced_element.php new file mode 100644 index 00000000..052f1248 --- /dev/null +++ b/src/Xml/Reader/Matcher/namespaced_element.php @@ -0,0 +1,20 @@ +current(); + + return $current->localName() === $localName && $current->namespace() === $namespace; + }; +} diff --git a/src/Xml/Reader/Matcher/node_attribute.php b/src/Xml/Reader/Matcher/node_attribute.php index f6e0cd29..5e556717 100644 --- a/src/Xml/Reader/Matcher/node_attribute.php +++ b/src/Xml/Reader/Matcher/node_attribute.php @@ -10,6 +10,7 @@ use function Psl\Iter\any; /** + * @deprecated Use attribute_value instead! This will be removed in next major version * @return \Closure(NodeSequence): bool */ function node_attribute(string $key, string $value): Closure diff --git a/src/Xml/Reader/Matcher/node_name.php b/src/Xml/Reader/Matcher/node_name.php index ceec497c..645fb010 100644 --- a/src/Xml/Reader/Matcher/node_name.php +++ b/src/Xml/Reader/Matcher/node_name.php @@ -8,6 +8,7 @@ use VeeWee\Xml\Reader\Node\NodeSequence; /** + * @deprecated Use element_name instead! This will be removed in next major version * @return \Closure(NodeSequence): bool */ function node_name(string $name): Closure diff --git a/src/Xml/Reader/Matcher/sequence.php b/src/Xml/Reader/Matcher/sequence.php new file mode 100644 index 00000000..4fe6e3d2 --- /dev/null +++ b/src/Xml/Reader/Matcher/sequence.php @@ -0,0 +1,41 @@ + $matcherSequence + * + * @return \Closure(NodeSequence): bool + */ +function sequence(callable ... $matcherSequence): Closure +{ + return static function (NodeSequence $sequence) use ($matcherSequence) : bool { + $nodeSequence = $sequence->sequence(); + if (count($matcherSequence) !== count($nodeSequence)) { + return false; + } + + $currentSequence = new NodeSequence(); + foreach ($nodeSequence as $i => $node) { + $currentSequence = $currentSequence->append($node); + $matcher = $matcherSequence[$i]; + if (!$matcher($currentSequence)) { + return false; + } + } + + return true; + }; +} diff --git a/src/bootstrap.php b/src/bootstrap.php index 607c4caf..6cdd0ec6 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -124,8 +124,21 @@ require_once __DIR__.'/Xml/Reader/Loader/xml_file_loader.php'; require_once __DIR__.'/Xml/Reader/Loader/xml_string_loader.php'; require_once __DIR__.'/Xml/Reader/Matcher/all.php'; +require_once __DIR__.'/Xml/Reader/Matcher/any.php'; +require_once __DIR__.'/Xml/Reader/Matcher/attribute_local_name.php'; +require_once __DIR__.'/Xml/Reader/Matcher/attribute_local_value.php'; +require_once __DIR__.'/Xml/Reader/Matcher/attribute_name.php'; +require_once __DIR__.'/Xml/Reader/Matcher/attribute_value.php'; +require_once __DIR__.'/Xml/Reader/Matcher/document_element.php'; +require_once __DIR__.'/Xml/Reader/Matcher/element_local_name.php'; +require_once __DIR__.'/Xml/Reader/Matcher/element_name.php'; +require_once __DIR__.'/Xml/Reader/Matcher/element_position.php'; +require_once __DIR__.'/Xml/Reader/Matcher/namespaced_attribute.php'; +require_once __DIR__.'/Xml/Reader/Matcher/namespaced_attribute_value.php'; +require_once __DIR__.'/Xml/Reader/Matcher/namespaced_element.php'; require_once __DIR__.'/Xml/Reader/Matcher/node_attribute.php'; require_once __DIR__.'/Xml/Reader/Matcher/node_name.php'; +require_once __DIR__.'/Xml/Reader/Matcher/sequence.php'; require_once __DIR__.'/Xml/Writer/Builder/attribute.php'; require_once __DIR__.'/Xml/Writer/Builder/attributes.php'; require_once __DIR__.'/Xml/Writer/Builder/children.php'; diff --git a/tests/Xml/Reader/Matcher/AbstractMatcherTest.php b/tests/Xml/Reader/Matcher/AbstractMatcherTest.php new file mode 100644 index 00000000..17ae66a3 --- /dev/null +++ b/tests/Xml/Reader/Matcher/AbstractMatcherTest.php @@ -0,0 +1,42 @@ + $expected + */ + public function test_real_xml_cases(Closure $matcher, string $xml, array $expected) + { + $reader = Reader::fromXmlString($xml); + $actual = [...$reader->provide($matcher)]; + + static::assertSame($actual, $expected); + } + + /** + * @dataProvider provideMatcherCases + * + * @param \Closure(NodeSequence): bool $matcher + */ + public function test_matcher_cases(Closure $matcher, NodeSequence $sequence, bool $expected) + { + $actual = $matcher($sequence); + + static::assertSame($actual, $expected); + } +} diff --git a/tests/Xml/Reader/Matcher/AllTest.php b/tests/Xml/Reader/Matcher/AllTest.php index 1de4bc77..bdf824ae 100644 --- a/tests/Xml/Reader/Matcher/AllTest.php +++ b/tests/Xml/Reader/Matcher/AllTest.php @@ -4,42 +4,76 @@ namespace VeeWee\Tests\Xml\Reader\Matcher; -use PHPUnit\Framework\TestCase; +use Generator; use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\all; +use function VeeWee\Xml\Reader\Matcher\element_name; -final class AllTest extends TestCase +final class AllTest extends AbstractMatcherTest { - public function test_it_returns_true_if_all_matchers_agree(): void + public static function provideRealXmlCases(): Generator { - $matcher = all( - static fn () => true, - static fn () => true, - static fn () => true - ); - static::assertTrue($matcher($this->createSequence())); + yield 'all' => [ + all(), + $xml = <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + $xml, + 'Jos', + 'Bos', + 'Mos' + ] + ]; + yield 'users' => [ + all(element_name('user')), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Bos', + 'Mos' + ] + ]; } - - public function test_it_returns_false_if__not_all_matchers_agree(): void + public static function provideMatcherCases(): Generator { - $matcher = all( - static fn () => true, - static fn () => true, - static fn () => false - ); - static::assertFalse($matcher($this->createSequence())); - } + $sequence = new NodeSequence(); - - public function test_it_returns_true_if_there_are_no_matchers(): void - { - $matcher = all(); - static::assertTrue($matcher($this->createSequence())); - } + yield 'it_returns_true_if_all_matchers_agree' => [ + all( + static fn () => true, + static fn () => true, + static fn () => true + ), + $sequence, + true + ]; - private function createSequence(): NodeSequence - { - return new NodeSequence(); + yield 'it_returns_false_if_not_all_matchers_agree' => [ + all( + static fn () => true, + static fn () => true, + static fn () => false + ), + $sequence, + false + ]; + + yield 'it_returns_true_if_there_are_no_matchers' => [ + all(), + $sequence, + true + ]; } } diff --git a/tests/Xml/Reader/Matcher/AnyTest.php b/tests/Xml/Reader/Matcher/AnyTest.php new file mode 100644 index 00000000..3fb9aff2 --- /dev/null +++ b/tests/Xml/Reader/Matcher/AnyTest.php @@ -0,0 +1,84 @@ + [ + any(), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [] + ]; + yield 'users' => [ + any(element_name('user')), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Bos', + 'Mos' + ] + ]; + } + + public static function provideMatcherCases(): Generator + { + $sequence = new NodeSequence(); + + yield 'it_returns_true_if_all_matchers_agree' => [ + any( + static fn () => true, + static fn () => true, + static fn () => true + ), + $sequence, + true + ]; + + yield 'it_returns_true_if_any_matchers_agree' => [ + any( + static fn () => false, + static fn () => true, + static fn () => false + ), + $sequence, + true + ]; + + yield 'it_returns_false_if_no_matchers_agree' => [ + any( + static fn () => false, + static fn () => false, + static fn () => false + ), + $sequence, + false + ]; + + yield 'it_returns_false_if_there_are_no_matchers' => [ + any(), + $sequence, + false + ]; + } +} diff --git a/tests/Xml/Reader/Matcher/AttributeLocalNameTest.php b/tests/Xml/Reader/Matcher/AttributeLocalNameTest.php new file mode 100644 index 00000000..6c0e635a --- /dev/null +++ b/tests/Xml/Reader/Matcher/AttributeLocalNameTest.php @@ -0,0 +1,68 @@ + [ + attribute_local_name('country'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Bos', + 'Mos', + ] + ]; + yield 'namespaced' => [ + attribute_local_name('country'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Bos', + ] + ]; + } + + public static function provideMatcherCases(): Generator + { + $sequence = new NodeSequence( + new ElementNode(1, 'x:item', 'item', 'https://x', 'x', [ + new AttributeNode('x:locale', 'locale', 'x', 'https://x', 'nl') + ]) + ); + + yield 'it_returns_true_if_local_attribute_name_matches' => [ + attribute_local_name('locale'), + $sequence, + true + ]; + + yield 'it_returns_false_if_local_attribute_name_does_not_match' => [ + attribute_local_name('unknown'), + $sequence, + false + ]; + } +} diff --git a/tests/Xml/Reader/Matcher/AttributeLocalValueTest.php b/tests/Xml/Reader/Matcher/AttributeLocalValueTest.php new file mode 100644 index 00000000..a3d9edc5 --- /dev/null +++ b/tests/Xml/Reader/Matcher/AttributeLocalValueTest.php @@ -0,0 +1,73 @@ + [ + attribute_local_value('country', 'BE'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Mos', + ] + ]; + yield 'namespaced' => [ + attribute_local_value('country', 'BE'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Mos' + ] + ]; + } + + public static function provideMatcherCases(): Generator + { + $sequence = new NodeSequence( + new ElementNode(1, 'x:item', 'item', 'https://x', 'x', [ + new AttributeNode('x:locale', 'locale', 'x', 'https://x', 'nl') + ]) + ); + + yield 'it_returns_true_if_local_attribute_value_matches' => [ + attribute_local_value('locale', 'nl'), + $sequence, + true + ]; + + yield 'it_returns_false_if_local_attribute_value_does_not_match' => [ + attribute_local_value('locale', 'en'), + $sequence, + false + ]; + + yield 'it_returns_false_if_local_attribute_value_is_not_available' => [ + attribute_local_value('unkown', 'en'), + $sequence, + false + ]; + } +} diff --git a/tests/Xml/Reader/Matcher/AttributeNameTest.php b/tests/Xml/Reader/Matcher/AttributeNameTest.php new file mode 100644 index 00000000..6753c8fa --- /dev/null +++ b/tests/Xml/Reader/Matcher/AttributeNameTest.php @@ -0,0 +1,67 @@ + [ + attribute_name('country'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Mos', + ] + ]; + yield 'namespaced' => [ + attribute_name('u:country'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Mos' + ] + ]; + } + + public static function provideMatcherCases(): Generator + { + $sequence = new NodeSequence( + new ElementNode(1, 'item', 'item', '', '', [ + new AttributeNode('locale', 'locale', '', '', 'nl') + ]) + ); + + yield 'it_returns_true_if_attribute_name_matches' => [ + attribute_name('locale'), + $sequence, + true + ]; + + yield 'it_returns_false_if_attribute_name_is_not_available' => [ + attribute_name('unkown'), + $sequence, + false + ]; + } +} diff --git a/tests/Xml/Reader/Matcher/AttributeValueTest.php b/tests/Xml/Reader/Matcher/AttributeValueTest.php new file mode 100644 index 00000000..faf05234 --- /dev/null +++ b/tests/Xml/Reader/Matcher/AttributeValueTest.php @@ -0,0 +1,73 @@ + [ + attribute_value('country', 'BE'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Mos', + ] + ]; + yield 'namespaced' => [ + attribute_value('u:country', 'BE'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Mos' + ] + ]; + } + + public static function provideMatcherCases(): Generator + { + $sequence = new NodeSequence( + new ElementNode(1, 'item', 'item', '', '', [ + new AttributeNode('locale', 'locale', '', '', 'nl') + ]) + ); + + yield 'it_returns_true_if_attribute_value_matches' => [ + attribute_value('locale', 'nl'), + $sequence, + true + ]; + + yield 'it_returns_false_if_attribute_value_does_not_match' => [ + attribute_value('locale', 'en'), + $sequence, + false + ]; + + yield 'it_returns_false_if_attribute_value_is_not_available' => [ + attribute_value('unkown', 'en'), + $sequence, + false + ]; + } +} diff --git a/tests/Xml/Reader/Matcher/DocumentElementTest.php b/tests/Xml/Reader/Matcher/DocumentElementTest.php new file mode 100644 index 00000000..36465c13 --- /dev/null +++ b/tests/Xml/Reader/Matcher/DocumentElementTest.php @@ -0,0 +1,52 @@ + [ + document_element(), + $xml = <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + $xml, + ] + ]; + } + + public static function provideMatcherCases(): Generator + { + $sequence = new NodeSequence(); + + yield 'it_returns_true_if_its_the_document_element' => [ + document_element(), + new NodeSequence( + new ElementNode(1, 'root', 'root', '', '', []) + ), + true + ]; + + yield 'it_returns_false_if_its_not_the_document_element' => [ + document_element(), + new NodeSequence( + new ElementNode(1, 'root', 'root', '', '', []), + new ElementNode(1, 'item', 'item', '', '', []), + ), + false + ]; + } +} diff --git a/tests/Xml/Reader/Matcher/ElementLocalNameTest.php b/tests/Xml/Reader/Matcher/ElementLocalNameTest.php new file mode 100644 index 00000000..53c0d761 --- /dev/null +++ b/tests/Xml/Reader/Matcher/ElementLocalNameTest.php @@ -0,0 +1,66 @@ + [ + element_local_name('user'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Bos', + 'Mos' + ] + ]; + yield 'namespaced' => [ + element_local_name('user'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Bos', + 'Mos' + ] + ]; + } + + public static function provideMatcherCases(): Generator + { + $sequence = new NodeSequence( + new ElementNode(1, 'x:item', 'item', 'https://x', 'x', []) + ); + + yield 'it_returns_true_if_local_element_name_matches' => [ + element_local_name('item'), + $sequence, + true + ]; + + yield 'it_returns_false_if_local_element_name_does_not_match' => [ + element_local_name('other'), + $sequence, + false + ]; + } +} diff --git a/tests/Xml/Reader/Matcher/ElementNameTest.php b/tests/Xml/Reader/Matcher/ElementNameTest.php new file mode 100644 index 00000000..a806a16d --- /dev/null +++ b/tests/Xml/Reader/Matcher/ElementNameTest.php @@ -0,0 +1,66 @@ + [ + element_name('user'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Bos', + 'Mos' + ] + ]; + yield 'namespaced' => [ + element_name('u:user'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Bos', + 'Mos' + ] + ]; + } + + public static function provideMatcherCases(): Generator + { + $sequence = new NodeSequence( + new ElementNode(1, 'item', 'item', '', '', []) + ); + + yield 'it_returns_true_if_element_name_matches' => [ + element_name('item'), + $sequence, + true + ]; + + yield 'it_returns_false_if_element_name_does_not_match' => [ + element_name('other'), + $sequence, + false + ]; + } +} diff --git a/tests/Xml/Reader/Matcher/ElementPositionTest.php b/tests/Xml/Reader/Matcher/ElementPositionTest.php new file mode 100644 index 00000000..3d298f4b --- /dev/null +++ b/tests/Xml/Reader/Matcher/ElementPositionTest.php @@ -0,0 +1,49 @@ + [ + element_position(2), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Bos', + ] + ]; + } + + public static function provideMatcherCases(): Generator + { + yield 'it_returns_true_if_element_position_matches' => [ + element_position(1), + new NodeSequence( + new ElementNode(1, 'item', 'item', '', '', []) + ), + true + ]; + + yield 'it_returns_false_if_element_name_does_not_match' => [ + element_position(1), + new NodeSequence( + new ElementNode(2, 'item', 'item', '', '', []) + ), + false + ]; + } +} diff --git a/tests/Xml/Reader/Matcher/NamespacedAttributeTest.php b/tests/Xml/Reader/Matcher/NamespacedAttributeTest.php new file mode 100644 index 00000000..5c3218e4 --- /dev/null +++ b/tests/Xml/Reader/Matcher/NamespacedAttributeTest.php @@ -0,0 +1,59 @@ + [ + namespaced_attribute('https://users', 'country'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Mos' + ] + ]; + } + + public static function provideMatcherCases(): Generator + { + $sequence = new NodeSequence( + new ElementNode(1, 'item', 'item', '', '', [ + new AttributeNode('locale', 'locale', 'https://x', 'x', 'nl') + ]) + ); + + yield 'it_returns_true_if_attribute_name_matches' => [ + namespaced_attribute('https://x', 'locale'), + $sequence, + true + ]; + + yield 'it_returns_false_if_attribute_name_is_not_available' => [ + namespaced_attribute('https://x', 'unknown'), + $sequence, + false + ]; + + yield 'it_returns_false_if_namespace_does_not_match' => [ + namespaced_attribute('https://invalid', 'locale'), + $sequence, + false + ]; + } +} diff --git a/tests/Xml/Reader/Matcher/NamespacedAttributeValueTest.php b/tests/Xml/Reader/Matcher/NamespacedAttributeValueTest.php new file mode 100644 index 00000000..acf5aa84 --- /dev/null +++ b/tests/Xml/Reader/Matcher/NamespacedAttributeValueTest.php @@ -0,0 +1,64 @@ + [ + namespaced_attribute_value('https://users', 'country', 'BE'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + ] + ]; + } + + public static function provideMatcherCases(): Generator + { + $sequence = new NodeSequence( + new ElementNode(1, 'item', 'item', '', '', [ + new AttributeNode('locale', 'locale', 'https://x', 'x', 'nl') + ]) + ); + + yield 'it_returns_true_if_attribute_name_matches' => [ + namespaced_attribute_value('https://x', 'locale', 'nl'), + $sequence, + true + ]; + + yield 'it_returns_false_if_attribute_name_is_not_available' => [ + namespaced_attribute_value('https://x', 'unknown', 'nl'), + $sequence, + false + ]; + + yield 'it_returns_false_if_namespace_does_not_match' => [ + namespaced_attribute_value('https://invalid', 'locale', 'nl'), + $sequence, + false + ]; + + yield 'it_returns_false_if_value_does_not_match' => [ + namespaced_attribute_value('https://x', 'locale', 'other'), + $sequence, + false + ]; + } +} diff --git a/tests/Xml/Reader/Matcher/NamespacedElementNameTest.php b/tests/Xml/Reader/Matcher/NamespacedElementNameTest.php new file mode 100644 index 00000000..eff410b7 --- /dev/null +++ b/tests/Xml/Reader/Matcher/NamespacedElementNameTest.php @@ -0,0 +1,56 @@ + [ + namespaced_element('https://users', 'user'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Mos' + ] + ]; + } + + public static function provideMatcherCases(): Generator + { + $sequence = new NodeSequence( + new ElementNode(1, 'x:item', 'item', 'https://x', 'x', []) + ); + + yield 'it_returns_true_if_element_name_matches' => [ + namespaced_element('https://x', 'item'), + $sequence, + true + ]; + + yield 'it_returns_false_if_element_name_does_not_match' => [ + namespaced_element('https://x', 'other'), + $sequence, + false + ]; + + yield 'it_returns_false_if_element_namespace_does_not_match' => [ + namespaced_element('https://invalid', 'item'), + $sequence, + false + ]; + } +} diff --git a/tests/Xml/Reader/Matcher/NodeAttributeTest.php b/tests/Xml/Reader/Matcher/NodeAttributeTest.php index 02e1d307..63a92fc2 100644 --- a/tests/Xml/Reader/Matcher/NodeAttributeTest.php +++ b/tests/Xml/Reader/Matcher/NodeAttributeTest.php @@ -4,40 +4,73 @@ namespace VeeWee\Tests\Xml\Reader\Matcher; -use PHPUnit\Framework\TestCase; +use Generator; use VeeWee\Xml\Reader\Node\AttributeNode; use VeeWee\Xml\Reader\Node\ElementNode; use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\node_attribute; -final class NodeAttributeTest extends TestCase +/** + * @deprecated Use attribute_value instead! This will be removed in next major version + */ +final class NodeAttributeTest extends AbstractMatcherTest { - public function test_it_returns_true_if_node_attribute_matches(): void + public static function provideRealXmlCases(): Generator { - $matcher = node_attribute('locale', 'nl'); - static::assertTrue($matcher($this->createSequence())); + yield 'users' => [ + node_attribute('country', 'BE'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Mos', + ] + ]; + yield 'namespaced' => [ + node_attribute('u:country', 'BE'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Mos' + ] + ]; } - - public function test_it_returns_false_if_node_attribute_does_not_match(): void + public static function provideMatcherCases(): Generator { - $matcher = node_attribute('locale', 'en'); - static::assertFalse($matcher($this->createSequence())); - } - - - public function test_it_returns_false_if_node_attribute_is_not_available(): void - { - $matcher = node_attribute('unkown', 'en'); - static::assertFalse($matcher($this->createSequence())); - } - - private function createSequence(): NodeSequence - { - return new NodeSequence( + $sequence = new NodeSequence( new ElementNode(1, 'item', 'item', '', '', [ new AttributeNode('locale', 'locale', '', '', 'nl') ]) ); + + yield 'it_returns_true_if_node_attribute_matches' => [ + node_attribute('locale', 'nl'), + $sequence, + true + ]; + + yield 'it_returns_false_if_node_attribute_does_not_match' => [ + node_attribute('locale', 'en'), + $sequence, + false + ]; + + yield 'it_returns_false_if_node_attribute_is_not_available' => [ + node_attribute('unkown', 'en'), + $sequence, + false + ]; } } diff --git a/tests/Xml/Reader/Matcher/NodeNameTest.php b/tests/Xml/Reader/Matcher/NodeNameTest.php index a21bc94f..f98b69c6 100644 --- a/tests/Xml/Reader/Matcher/NodeNameTest.php +++ b/tests/Xml/Reader/Matcher/NodeNameTest.php @@ -4,30 +4,66 @@ namespace VeeWee\Tests\Xml\Reader\Matcher; -use PHPUnit\Framework\TestCase; +use Generator; use VeeWee\Xml\Reader\Node\ElementNode; use VeeWee\Xml\Reader\Node\NodeSequence; use function VeeWee\Xml\Reader\Matcher\node_name; -final class NodeNameTest extends TestCase +/** + * @deprecated Use element_name instead! This will be removed in next major version + */ +final class NodeNameTest extends AbstractMatcherTest { - public function test_it_returns_true_if_node_name_matches(): void + public static function provideRealXmlCases(): Generator { - $matcher = node_name('item'); - static::assertTrue($matcher($this->createSequence())); + yield 'users' => [ + node_name('user'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Bos', + 'Mos' + ] + ]; + yield 'namespaced' => [ + node_name('u:user'), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + 'Bos', + 'Mos' + ] + ]; } - - public function test_it_returns_false_if_node_name_does_not_match(): void + public static function provideMatcherCases(): Generator { - $matcher = node_name('other'); - static::assertFalse($matcher($this->createSequence())); - } - - private function createSequence(): NodeSequence - { - return new NodeSequence( + $sequence = new NodeSequence( new ElementNode(1, 'item', 'item', '', '', []) ); + + yield 'it_returns_true_if_element_name_matches' => [ + node_name('item'), + $sequence, + true + ]; + + yield 'it_returns_false_if_element_name_does_not_match' => [ + node_name('other'), + $sequence, + false + ]; } } diff --git a/tests/Xml/Reader/Matcher/SequenceTest.php b/tests/Xml/Reader/Matcher/SequenceTest.php new file mode 100644 index 00000000..b12a00af --- /dev/null +++ b/tests/Xml/Reader/Matcher/SequenceTest.php @@ -0,0 +1,119 @@ + [ + sequence( + document_element(), + all( + element_name('user'), + attribute_value('locale', 'nl') + ) + ), + <<<'EOXML' + + Jos + Bos + Mos + + EOXML, + [ + 'Jos', + ] + ]; + } + + public static function provideMatcherCases(): Generator + { + yield 'it_returns_true_if_no_sequence_ant_matcher' => [ + sequence(), + new NodeSequence(), + true + ]; + + yield 'it_returns_false_on_invalid_count' => [ + sequence(static fn () => true), + new NodeSequence(), + false + ]; + + yield 'it_returns_false_on_invalid_step' => [ + sequence(static fn () => false), + new NodeSequence( + new ElementNode(1, 'item', 'item', '', '', []) + ), + false + ]; + + yield 'it_returns_false_on_invalid_step_in_between' => [ + sequence( + static fn () => true, + static fn () => false, + static fn () => true + ), + new NodeSequence( + new ElementNode(1, 'item', 'item', '', '', []), + new ElementNode(1, 'item', 'item', '', '', []), + new ElementNode(1, 'item', 'item', '', '', []) + ), + false + ]; + + yield 'it_returns_true_if_full_sequence_matches' => [ + sequence( + static fn () => true, + static fn () => true, + static fn () => true + ), + new NodeSequence( + new ElementNode(1, 'item', 'item', '', '', []), + new ElementNode(1, 'item', 'item', '', '', []), + new ElementNode(1, 'item', 'item', '', '', []) + ), + true + ]; + + yield 'it_returns_false_if_elements_dont_go_deep_enough' => [ + sequence( + static fn () => true, + static fn () => true, + static fn () => true + ), + new NodeSequence( + new ElementNode(1, 'item', 'item', '', '', []), + new ElementNode(1, 'item', 'item', '', '', []), + ), + false + ]; + + yield 'it_returns_false_if_elements_go_deeper' => [ + sequence( + static fn () => true, + static fn () => true, + static fn () => true + ), + new NodeSequence( + new ElementNode(1, 'item', 'item', '', '', []), + new ElementNode(1, 'item', 'item', '', '', []), + new ElementNode(1, 'item', 'item', '', '', []), + new ElementNode(1, 'item', 'item', '', '', []), + ), + false + ]; + } +}