<?php namespace tests\unit; use PHPUnit\Framework\TestCase; use r7r\ste\Parser; use r7r\ste\TextNode; use r7r\ste\VariableNode; use r7r\ste\TagNode; use r7r\ste\ParseCompileError; class ParserTest extends TestCase { /** * @dataProvider successfulParsingDataProvider */ public function testSuccessfulParsing(string $source, array $expected) { $actual = Parser::parse($source, '-'); self::assertEquals($expected, $actual); } public function successfulParsingDataProvider() { return [ ['', []], ['Hello', [ new TextNode('-', 0, 'Hello'), ]], ['$foo', [ new VariableNode('-', 0, 'foo', []), ]], //01234567890 ['foo$bar$baz', [ new TextNode('-', 0, 'foo'), new VariableNode('-', 3, 'bar', []), new VariableNode('-', 7, 'baz', []), ]], //012345678 ['${foo}bar', [ new VariableNode('-', 0, 'foo', []), new TextNode('-', 6, 'bar'), ]], //012345678 ['$foo[bar]', [ new VariableNode('-', 0, 'foo', [ [new TextNode('-', 5, 'bar')], ]), ]], //01234567890 ['${foo[bar]}', [ new VariableNode('-', 0, 'foo', [ [new TextNode('-', 6, 'bar')], ]), ]], //012345678 ['$foo[$bar]', [ new VariableNode('-', 0, 'foo', [ [new VariableNode('-', 5, 'bar', [])], ]), ]], //012345678901234 ['$foo[$bar[baz]]', [ new VariableNode('-', 0, 'foo', [ [new VariableNode('-', 5, 'bar', [ [new TextNode('-', 10, 'baz')], ])], ]), ]], //012345678901234 ['$foo[$bar][baz]', [ new VariableNode('-', 0, 'foo', [ [new VariableNode('-', 5, 'bar', [])], [new TextNode('-', 11, 'baz')] ]), ]], //0123456789012345678901 ['a${b[c$d[e${f}g]][h]}i', [ new TextNode('-', 0, 'a'), new VariableNode('-', 1, 'b', [ [ new TextNode('-', 5, 'c'), new VariableNode('-', 6, 'd', [ [ new TextNode('-', 9, 'e'), new VariableNode('-', 10, 'f', []), new TextNode('-', 14, 'g') ] ]) ], [new TextNode('-', 18, 'h')], ]), new TextNode('-', 21, 'i'), ]], ['<ste:foo />', [ new TagNode('-', 0, 'foo'), ]], ['<ste:foo></ste:foo>', [ new TagNode('-', 0, 'foo'), ]], //0123456789012345678901 ['<ste:foo>bar</ste:foo>', [ new TagNode('-', 0, 'foo', [], [ new TextNode('-', 9, 'bar'), ]), ]], //0 1 2 3 4 5 6 7 8 9 0 //012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234 ['<ste:foo a="$b[0][$x]" c="\"" d="${e}f"><ste:foo><ste:xyz>abc</ste:xyz><ste:foo />$x</ste:foo></ste:foo>x', [ new TagNode('-', 0, 'foo', [ 'a' => [ new VariableNode('-', 12, 'b', [ [new TextNode('-', 15, '0')], [new VariableNode('-', 18, 'x', [])], ]), ], 'c' => [ new TextNode('-', 26, '"'), ], 'd' => [ new VariableNode('-', 33, 'e', []), new TextNode('-', 37, 'f'), ] ], [ new TagNode('-', 40, 'foo', [], [ new TagNode('-', 49, 'xyz', [], [ new TextNode('-', 58, 'abc'), ]), new TagNode('-', 71, 'foo'), new VariableNode('-', 82, 'x', []), ]), ]), new TextNode('-', 104, 'x'), ]], //0 1 2 3 //01234567890123456789012345678901234567 ['foo?{~{$x|eq|\}$y}|b|<ste:foo/>\}}$bar', [ new TextNode('-', 0, 'foo'), new TagNode('-', 3, 'if', [], [ new TagNode('-', 5, 'cmp', [ 'text_a' => [new VariableNode('-', 7, 'x', [])], 'op' => [new TextNode('-', 10, 'eq')], 'text_b' => [ new TextNode('-', 13, '}'), new VariableNode('-', 15, 'y', []), ], ], []), new TagNode('-', 3, 'then', [], [ new TextNode('-', 19, 'b'), ]), new TagNode('-', 3, 'else', [], [ new TagNode('-', 21, 'foo'), new TextNode('-', 31, '}'), ]), ]), new VariableNode('-', 34, 'bar', []), ]], ['<ste:comment>ignored</ste:comment>', []], ['foo<ste:comment>ignored</ste:comment>bar', [ // These are not two TextNodes as the parser will collapse adjacent TextNodes new TextNode('-', 0, 'foobar'), ]], ['<ste:rawtext><ste:foo a="bla">$abc</ste:foo></ste:rawtext>', [ new TextNode('-', 0, '<ste:foo a="bla">$abc</ste:foo>'), ]], ['<ste:rawtext><ste:foo a="bla"><ste:rawtext>$abc</ste:foo></ste:rawtext>', [ new TextNode('-', 0, '<ste:foo a="bla"><ste:rawtext>$abc</ste:foo>'), ]], ]; } /** * @dataProvider failsToParseDataProvider * @param string $input */ public function testFailsToParse(string $input) { self::expectException(ParseCompileError::class); Parser::parse($input, '-'); } public function failsToParseDataProvider() { return [ // Incomplete tag ['<ste:foo'], ['<ste:'], ['<ste:>'], ['<ste:foo /'], // Incomplete closing tag ['<ste:foo>bar</'], ['<ste:foo>bar</ste'], ['<ste:foo>bar</ste:'], ['<ste:foo>bar</ste:foo'], ['<ste:foo></'], ['<ste:foo></ste'], ['<ste:foo></ste:'], ['<ste:foo></ste:foo'], // Missing parameter value ['<ste:foo bar= />'], // Unclosed parameter ['<ste:foo bar=" />'], ['<ste:foo bar="baz />'], // Unclosed tag ['<ste:foo>bar'], // Trailing closing tag ['</ste:foo>'], ['abc</ste:foo>'], ['<ste:foo></ste:foo></ste:foo>'], // Open/close tag mismatch ['<ste:foo>bar</ste:baz>'], // Nesting error ['<ste:foo><ste:bar></ste:foo></ste:bar>'], // Invalid parameter name ['<ste:foo $bar />'], // Unclosed variable ['${foo'], ['${${foo}'], // Unclosed array ['$foo[bar'], // Incomplete variable ['$'], ['foo$'], ['foo${}'], // Incomplete shorthands ['?{foo|bar}'], ['?{foo|bar|baz'], ['?{foo|'], ['?{foo}'], ['?{'], ['~{foo|bar}'], ['~{foo|bar|baz'], ['~{foo|'], ['~{foo}'], ['~{'], // Unclosing pseudotags ['<ste:comment>foo'], ['<ste:rawtext>foo'], ]; } }