* * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ namespace Symfony\CS\Tests\Tokenizer; use Symfony\CS\Tokenizer\Token; use Symfony\CS\Tokenizer\Tokens; /** * @author Dariusz RumiƄski * @author Max Voloshin * @author Gregor Harlan */ class TokensTest extends \PHPUnit_Framework_TestCase { /** * @param Token[]|null $expected * @param Token[]|null $input */ private function assertEqualsTokensArray(array $expected = null, array $input = null) { if (null === $expected) { $this->assertNull($input); return; } $this->assertSame(array_keys($expected), array_keys($input), 'Both arrays need to have same keys.'); foreach ($expected as $index => $expectedToken) { $this->assertTrue( $expectedToken->equals($input[$index]), sprintf('The token at index %d should be %s, got %s', $index, $expectedToken->toJson(), $input[$index]->toJson()) ); } } public function testGetClassyElements() { $source = <<<'PHP' getClassyElements()); $this->assertCount(6, $elements); $this->assertSame('property', $elements[0]['type']); $this->assertSame('property', $elements[1]['type']); $this->assertSame('property', $elements[2]['type']); $this->assertSame('property', $elements[3]['type']); $this->assertSame('method', $elements[4]['type']); $this->assertSame('method', $elements[5]['type']); } public function testReadFromCacheAfterClearing() { $code = 'count(); for ($i = 0; $i < $countBefore; ++$i) { $tokens[$i]->clear(); } $tokens = Tokens::fromCode($code); $this->assertSame($countBefore, $tokens->count()); } /** * @dataProvider provideIsAnonymousClassCases */ public function testIsAnonymousClass($source, array $expected) { $tokens = Tokens::fromCode($source); foreach ($expected as $index => $expectedValue) { $this->assertSame($expectedValue, $tokens->isAnonymousClass($index)); } } public function provideIsAnonymousClassCases() { return array( array( ' false), ), array( ' true), ), array( ' true), ), array( ' false, 19 => true), ), array( 'a) implements B{}) extends C{};', array(7 => true, 11 => true), ), ); } /** * @dataProvider provideIsLambdaCases */ public function testIsLambda($source, array $expected) { $tokens = Tokens::fromCode($source); foreach ($expected as $index => $expectedValue) { $this->assertSame($expectedValue, $tokens->isLambda($index)); } } public function provideIsLambdaCases() { return array( array( ' false), ), array( ' false), ), array( ' true), ), array( ' true), ), array( ' true), ), array( ' true), ), ); } /** * @dataProvider provideIsShortArrayCases */ public function testIsShortArray($source, array $expected) { $tokens = Tokens::fromCode($source); foreach ($expected as $index => $expectedValue) { $this->assertSame($expectedValue, $tokens->isShortArray($index)); } } public function provideIsShortArrayCases() { return array( array( ' true), ), array( ' true), ), array( ' true, 2 => true), ), array( ' true, 5 => true), ), array( ' true), ), array( ' true, 6 => false), ), array( ' false), ), array( ' false), ), array( ' false), ), array( ' false), ), array( ' false), ), array( ' false), ), array( ' false), ), array( ' false), ), array( ' false), ), ); } /** * @dataProvider provideIsUnarySuccessorOperator */ public function testIsUnarySuccessorOperator($source, array $expected) { $tokens = Tokens::fromCode($source); foreach ($expected as $index => $expectedValue) { $this->assertSame($expectedValue, $tokens->isUnarySuccessorOperator($index)); if ($expectedValue) { $this->assertFalse($tokens->isUnaryPredecessorOperator($index)); $this->assertFalse($tokens->isBinaryOperator($index)); } } } public function provideIsUnarySuccessorOperator() { return array( array( ' true), ), array( ' true), ), array( ' true), ), array( ' true, 4 => false), ), array( ' true), ), array( 'bar++;', array(4 => true), ), array( '{"bar"}++;', array(6 => true), ), array( ' true), ), ); } /** * @dataProvider provideIsUnaryPredecessorOperator */ public function testIsUnaryPredecessorOperator($source, array $expected) { $tokens = Tokens::fromCode($source); foreach ($expected as $index => $expectedValue) { $this->assertSame($expectedValue, $tokens->isUnaryPredecessorOperator($index)); if ($expectedValue) { $this->assertFalse($tokens->isUnarySuccessorOperator($index)); $this->assertFalse($tokens->isBinaryOperator($index)); } } } public function provideIsUnaryPredecessorOperator() { return array( array( ' true), ), array( ' true), ), array( ' true), ), array( ' false, 5 => true), ), array( ' true, 2 => true), ), array( ' true), ), array( ' true), ), array( ' true), ), array( ' true, 8 => true), ), array( ' true, 11 => true, 17 => true), ), ); } /** * @dataProvider provideIsUnaryPredecessorOperator56 * @requires PHP 5.6 */ public function testIsUnaryPredecessorOperator56($source, array $expected) { $tokens = Tokens::fromCode($source); foreach ($expected as $index => $expectedValue) { $this->assertSame($expectedValue, $tokens->isUnaryPredecessorOperator($index)); if ($expectedValue) { $this->assertFalse($tokens->isUnarySuccessorOperator($index)); $this->assertFalse($tokens->isBinaryOperator($index)); } } } public function provideIsUnaryPredecessorOperator56() { return array( array( ' true), ), array( ' true, 6 => true), ), array( ' true), ), array( ' true), ), array( ' true), ), ); } /** * @dataProvider provideIsBinaryOperator */ public function testIsBinaryOperator($source, array $expected) { $tokens = Tokens::fromCode($source); foreach ($expected as $index => $expectedValue) { $this->assertSame($expectedValue, $tokens->isBinaryOperator($index)); if ($expectedValue) { $this->assertFalse($tokens->isUnarySuccessorOperator($index)); $this->assertFalse($tokens->isUnaryPredecessorOperator($index)); } } } public function provideIsBinaryOperator() { $cases = array( array( ' true), ), array( ' true), ), array( ' true), ), array( ' true), ), array( ' true), ), array( ' true), ), array( ' true), ), array( ' true), ), array( ' true), ), array( ' true), ), array( ' true), ), array( ' "c", );', array(3 => true, 9 => true, 12 => false), ), array( ' true, 5 => false), ), array( ' true, 5 => false, 8 => true, 10 => false), ), array( ' true, 5 => false), ), array( ' false, 4 => true), ), array( ' true), ), array( ' true), ), array( ' true), ), ); $operators = array( '+', '-', '*', '/', '%', '<', '>', '|', '^', '&=', '&&', '||', '.=', '/=', '==', '>=', '===', '!=', '<>', '!==', '<=', 'and', 'or', 'xor', '-=', '%=', '*=', '|=', '+=', '<<', '<<=', '>>', '>>=', '^', ); foreach ($operators as $operator) { $cases[] = array( ' true), ); } return $cases; } /** * @dataProvider provideIsBinaryOperator56 * @requires PHP 5.6 */ public function testIsBinaryOperator56($source, array $expected) { $tokens = Tokens::fromCode($source); foreach ($expected as $index => $expectedValue) { $this->assertSame($expectedValue, $tokens->isBinaryOperator($index)); if ($expectedValue) { $this->assertFalse($tokens->isUnarySuccessorOperator($index)); $this->assertFalse($tokens->isUnaryPredecessorOperator($index)); } } } public function provideIsBinaryOperator56() { return array( array( ' true), ), array( ' true), ), ); } /** * @dataProvider provideIsBinaryOperator70 * @requires PHP 7.0 */ public function testIsBinaryOperator70($source, array $expected) { $tokens = Tokens::fromCode($source); foreach ($expected as $index => $expectedValue) { $this->assertSame($expectedValue, $tokens->isBinaryOperator($index)); if ($expectedValue) { $this->assertFalse($tokens->isUnarySuccessorOperator($index)); $this->assertFalse($tokens->isUnaryPredecessorOperator($index)); } } } public function provideIsBinaryOperator70() { return array( array( ' $b;', array(3 => true), ), array( ' true), ), ); } /** * @dataProvider provideFindSequence */ public function testFindSequence($source, $expected, array $params) { $tokens = Tokens::fromCode($source); $this->assertEqualsTokensArray($expected, call_user_func_array(array($tokens, 'findSequence'), $params)); } public function provideFindSequence() { return array( array( ' new Token(array(T_OPEN_TAG, ' new Token(array(T_VARIABLE, '$x', 1)), ), array(array( array(T_OPEN_TAG), array(T_VARIABLE, '$x'), )), ), array( ' new Token('='), 5 => new Token(array(T_LNUMBER, '1', 1)), 6 => new Token(';'), ), array(array( '=', array(T_LNUMBER, '1'), ';', )), ), array( ' new Token(array(T_OPEN_TAG, ' new Token(array(T_VARIABLE, '$x', 1)), ), array(array( array(T_OPEN_TAG), array(T_VARIABLE, '$x'), ), 0), ), array( ' new Token('='), 5 => new Token(array(T_LNUMBER, '1', 1)), 6 => new Token(';'), ), array(array( '=', array(T_LNUMBER, '1'), ';', ), 3, 6), ), array( ' new Token(array(T_OPEN_TAG, ' new Token(array(T_VARIABLE, '$x', 1)), ), array(array( array(T_OPEN_TAG), array(T_VARIABLE, '$x'), ), 0, 1, true), ), array( ' new Token(array(T_OPEN_TAG, ' new Token(array(T_VARIABLE, '$x', 1)), ), array(array( array(T_OPEN_TAG), array(T_VARIABLE, '$X'), ), 0, 1, false), ), array( ' new Token(array(T_OPEN_TAG, ' new Token(array(T_VARIABLE, '$x', 1)), ), array(array( array(T_OPEN_TAG), array(T_VARIABLE, '$X'), ), 0, 1, array(true, false)), ), array( ' new Token(array(T_OPEN_TAG, ' new Token(array(T_VARIABLE, '$x', 1)), ), array(array( array(T_OPEN_TAG), array(T_VARIABLE, '$X'), ), 0, 1, array(1 => false)), ), array( ' false)), ), ); } /** * @expectedException \InvalidArgumentException * @dataProvider provideFindSequenceExceptions */ public function testFindSequenceException($message, $sequence) { $tokens = Tokens::fromCode('findSequence($sequence); } catch (\InvalidArgumentException $e) { $this->assertSame($message, $e->getMessage()); throw $e; } } public function provideFindSequenceExceptions() { $emptyToken = new Token('!'); $emptyToken->clear(); return array( array('Invalid sequence', array()), array('Non-meaningful token at position: 0', array( array(T_WHITESPACE, ' '), )), array('Non-meaningful token at position: 1', array( '{', array(T_COMMENT, '// Foo'), '}', )), array('Non-meaningful token at position: 2', array( '{', '!', $emptyToken, '}', )), ); } public function testClearRange() { $source = <<<'PHP' findGivenKind(T_PUBLIC)); $fooIndex = $publicIndexes[0]; $barIndex = $publicIndexes[1]; $tokens->clearRange($fooIndex, $barIndex - 1); $newPublicIndexes = array_keys($tokens->findGivenKind(T_PUBLIC)); $this->assertSame($barIndex, reset($newPublicIndexes)); for ($i = $fooIndex; $i < $barIndex; ++$i) { $this->assertTrue($tokens[$i]->isWhitespace()); } } /** * @dataProvider provideMonolithicPhpDetection * * @param string $source * @param bool $monolitic */ public function testMonolithicPhpDetection($source, $monolitic) { $tokens = Tokens::fromCode($source); $this->assertSame($monolitic, $tokens->isMonolithicPhp()); } public function provideMonolithicPhpDetection() { return array( array("", true), array('', false), array(' ', false), array("#!/usr/bin/env php\n ", false), array("assertSame($monolitic, $tokens->isMonolithicPhp()); } public function provideShortOpenTagMonolithicPhpDetection() { return array( array("", true), array(" ", false), array(" PHP_VERSION_ID && !defined('HHVM_VERSION')) { // Short open tag echo is parsed as T_INLINE_HTML $monolitic = false; } $tokens = Tokens::fromCode($source); $this->assertSame($monolitic, $tokens->isMonolithicPhp()); } public function provideShortOpenTagEchoMonolithicPhpDetection() { return array( array("", true), array(" ", false), array("assertTrue($tokens->isArray($tokenIndex), 'Expected to be an array.'); $this->assertSame($isMultilineArray, $tokens->isArrayMultiLine($tokenIndex), sprintf('Expected %sto be a multiline array', $isMultilineArray ? '' : 'not ')); $this->assertSame($isShortArray, $tokens->isShortArray($tokenIndex), sprintf('Expected %sto be a short array', $isShortArray ? '' : 'not ')); } public function provideIsArray() { $cases = array( array( ' 1); ', 2, ), array( // short array PHP 5.4 single line ' 2]; ', 2, false, true, ), array( ' 3 ); ', 2, true, ), array( // short array PHP 5.4 multi line ' 4 ]; ', 2, true, true, ), array( ' array(5, 6, 7), 8 => new \Exception(\'Ellow\') ); ', 2, true, ), array( // mix short array syntax ' [9, 10, 11], 12 => new \Exception(\'Ellow\') ); ', 2, true, ), // Windows/Max EOL testing array( " 13);\r\n", 1, ), array( " 14,\r\n 'b' => 15\r\n );\r\n", 2, true, ), ); return $cases; } /** * @dataProvider provideArrayExceptions */ public function testIsNotArray($source, $tokenIndex) { $tokens = Tokens::fromCode($source); $this->assertFalse($tokens->isArray($tokenIndex)); } /** * @dataProvider provideArrayExceptions */ public function testIsNotShortArray($source, $tokenIndex) { $tokens = Tokens::fromCode($source); $this->assertFalse($tokens->isShortArray($tokenIndex)); } /** * @expectedException \InvalidArgumentException * @dataProvider provideArrayExceptions */ public function testIsMultiLineArrayException($source, $tokenIndex) { $tokens = Tokens::fromCode($source); $tokens->isArrayMultiLine($tokenIndex); } public function provideArrayExceptions() { $cases = array( array('