* Dariusz RumiƄski * * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ namespace PhpCsFixer\Tests\Fixer\ClassNotation; use PhpCsFixer\Fixer\ClassNotation\ClassDefinitionFixer; use PhpCsFixer\Test\AbstractFixerTestCase; use PhpCsFixer\Tokenizer\Tokens; use PhpCsFixer\WhitespacesFixerConfig; /** * @author SpacePossum * * @internal * * @covers \PhpCsFixer\Fixer\ClassNotation\ClassDefinitionFixer */ final class ClassDefinitionFixerTest extends AbstractFixerTestCase { /** * @group legacy * @expectedDeprecation Passing NULL to set default configuration is deprecated and will not be supported in 3.0, use an empty array instead. */ public function testLegacyConfigureDefaultToNull() { $defaultConfig = [ 'multiLineExtendsEachSingleLine' => false, 'singleItemSingleLine' => false, 'singleLine' => false, ]; $fixer = new ClassDefinitionFixer(); $fixer->configure($defaultConfig); $this->assertAttributeSame($defaultConfig, 'configuration', $fixer); $fixer->configure(null); $this->assertAttributeSame($defaultConfig, 'configuration', $fixer); } public function testConfigureDefaultToNull() { $defaultConfig = [ 'multiLineExtendsEachSingleLine' => false, 'singleItemSingleLine' => false, 'singleLine' => false, ]; $fixer = new ClassDefinitionFixer(); $fixer->configure($defaultConfig); $this->assertAttributeSame($defaultConfig, 'configuration', $fixer); $fixer->configure([]); $this->assertAttributeSame($defaultConfig, 'configuration', $fixer); } /** * @param string $expected PHP source code * @param string $input PHP source code * @param array $config * * @dataProvider provideAnonymousClassesCases * * @requires PHP 7.0 */ public function testFixingAnonymousClasses($expected, $input, array $config = []) { $this->fixer->configure($config); $this->doTest($expected, $input); } /** * @param string $expected PHP source code * @param string $input PHP source code * * @dataProvider provideClassesCases */ public function testFixingClasses($expected, $input) { $this->fixer->configure([]); $this->doTest($expected, $input); } /** * @param string $expected PHP source code * @param string $input PHP source code * @param array $config * * @dataProvider provideClassesWithConfigCases */ public function testFixingClassesWithConfig($expected, $input, array $config) { $this->fixer->configure($config); $this->doTest($expected, $input); } /** * @param string $expected PHP source code * @param string $input PHP source code * * @dataProvider provideInterfacesCases */ public function testFixingInterfaces($expected, $input) { $this->fixer->configure([]); $this->doTest($expected, $input); } /** * @param string $expected PHP source code * @param string $input PHP source code * * @dataProvider provideTraitsCases */ public function testFixingTraits($expected, $input) { $this->fixer->configure([]); $this->doTest($expected, $input); } public function testInvalidConfigurationKey() { $this->setExpectedExceptionRegExp( \PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException::class, '/^\[class_definition\] Invalid configuration: The option "a" does not exist\. Defined options are: "multiLineExtendsEachSingleLine", "singleItemSingleLine", "singleLine"\.$/' ); $fixer = new ClassDefinitionFixer(); $fixer->configure(['a' => false]); } public function testInvalidConfigurationValueType() { $this->setExpectedExceptionRegExp( \PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException::class, '/^\[class_definition\] Invalid configuration: The option "singleLine" with value "z" is expected to be of type "bool", but is of type "string"\.$/' ); $fixer = new ClassDefinitionFixer(); $fixer->configure(['singleLine' => 'z']); } public function provideAnonymousClassesCases() { return [ [ ' true], ], [ " true], ], [ 'prop) {};', 'prop ){};', ], [ 'prop, $v[3], 4) {};', 'prop,$v[3], 4) {};', ], 'PSR-12 Extends/Implements Parenthesis on the next line.' => [ ' [ ' [ ' true], ], [ 'provideClassyCases('class'), $this->provideClassyExtendingCases('class'), $this->provideClassyImplementsCases() ); } public function provideClassesWithConfigCases() { return [ [ " true], ], [ " true], ], [ " false, 'singleItemSingleLine' => true], ], [ " true], ], [ " true], ], [ " true], ], [ " false, 'singleItemSingleLine' => true], ], [ " false, 'singleItemSingleLine' => false, 'multiLineExtendsEachSingleLine' => true, ], ], ]; } public function provideInterfacesCases() { $cases = array_merge( $this->provideClassyCases('interface'), $this->provideClassyExtendingCases('interface') ); $cases[] = [ 'provideClassyCases('trait'); } /** * @param string $source PHP source code * @param array $expected * * @dataProvider provideClassyDefinitionInfoCases */ public function testClassyDefinitionInfo($source, array $expected) { Tokens::clearCache(); $tokens = Tokens::fromCode($source); $method = new \ReflectionMethod($this->fixer, 'getClassyDefinitionInfo'); $method->setAccessible(true); $result = $method->invoke($this->fixer, $tokens, $expected['classy']); $this->assertSame($expected, $result); } public function provideClassyDefinitionInfoCases() { return [ [ ' 1, 'classy' => 1, 'open' => 4, 'extends' => false, 'implements' => false, 'anonymousClass' => false, ], ], [ ' 1, 'classy' => 3, 'open' => 6, 'extends' => false, 'implements' => false, 'anonymousClass' => false, ], ], [ ' 1, 'classy' => 5, 'open' => 8, 'extends' => false, 'implements' => false, 'anonymousClass' => false, ], ], [ ' 1, 'classy' => 1, 'open' => 9, 'extends' => [ 'start' => 5, 'numberOfExtends' => 1, 'multiLine' => false, ], 'implements' => false, 'anonymousClass' => false, ], ], [ ' 1, 'classy' => 1, 'open' => 13, 'extends' => [ 'start' => 5, 'numberOfExtends' => 3, 'multiLine' => false, ], 'implements' => false, 'anonymousClass' => false, ], ], ]; } /** * @param string $source PHP source code * @param string $label * @param array $expected * * @dataProvider provideClassyImplementsInfoCases */ public function testClassyInheritanceInfo($source, $label, array $expected) { $this->doTestClassyInheritanceInfo($source, $label, $expected); } /** * @param string $source PHP source code * @param string $label * @param array $expected * * @requires PHP 7.0 * @dataProvider provideClassyImplementsInfoCases7 */ public function testClassyInheritanceInfo7($source, $label, array $expected) { $this->doTestClassyInheritanceInfo($source, $label, $expected); } public function doTestClassyInheritanceInfo($source, $label, array $expected) { Tokens::clearCache(); $tokens = Tokens::fromCode($source); $this->assertTrue($tokens[$expected['start']]->isGivenKind([T_IMPLEMENTS, T_EXTENDS]), sprintf('Token must be "implements" or "extends", got "%s".', $tokens[$expected['start']]->getContent())); $method = new \ReflectionMethod($this->fixer, 'getClassyInheritanceInfo'); $method->setAccessible(true); $result = $method->invoke($this->fixer, $tokens, $expected['start'], $label); $this->assertSame($expected, $result); } public function provideClassyImplementsInfoCases() { return [ [ ' 5, 'numberOfImplements' => 3, 'multiLine' => false], ], [ ' 5, 'numberOfImplements' => 3, 'multiLine' => false], ], [ ' 5, 'numberOfImplements' => 1, 'multiLine' => false], ], [ " 5, 'numberOfImplements' => 2, 'multiLine' => true], ], [ " 5, 'numberOfImplements' => 3, 'multiLine' => false], ], [ 'test(); }', 'numberOfImplements', ['start' => 36, 'numberOfImplements' => 2, 'multiLine' => true], ], ]; } public function provideClassyImplementsInfoCases7() { return [ [ " 12, 'numberOfExtends' => 1, 'multiLine' => true], ], [ " 16, 'numberOfImplements' => 2, 'multiLine' => false], ], [ " 12, 'numberOfExtends' => 1, 'multiLine' => true], ], ]; } /** * @param string $expected * @param null|string $input * * @dataProvider providePHP7Cases * @requires PHP 7.0 */ public function testFixPHP7($expected, $input = null) { $this->fixer->configure([]); $this->doTest($expected, $input); } public function providePHP7Cases() { return [ [ '', '', ], ]; } /** * @param string $expected * @param null|string $input * * @dataProvider provideMessyWhitespacesCases */ public function testMessyWhitespaces($expected, $input = null) { $this->fixer->setWhitespacesConfig(new WhitespacesFixerConfig("\t", "\r\n")); $this->fixer->configure([]); $this->doTest($expected, $input); } public function provideMessyWhitespacesCases() { return [ [ "