123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- <?php
- /*
- * This file is part of PHP CS Fixer.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- * Dariusz Rumiński <dariusz.ruminski@gmail.com>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
- namespace PhpCsFixer\Tests\AutoReview;
- use PhpCsFixer\DocBlock\DocBlock;
- use PhpCsFixer\Tests\TestCase;
- use PhpCsFixer\Tokenizer\Tokens;
- use Symfony\Component\Finder\Finder;
- use Symfony\Component\Finder\SplFileInfo;
- /**
- * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
- *
- * @internal
- *
- * @coversNothing
- * @group auto-review
- */
- final class ProjectCodeTest extends TestCase
- {
- /**
- * This structure contains older classes that are not yet covered by tests.
- *
- * It may only shrink, never add anything to it.
- *
- * @var string[]
- */
- private static $classesWithoutTests = array(
- 'PhpCsFixer\Console\SelfUpdate\GithubClient',
- 'PhpCsFixer\Console\WarningsDetector',
- 'PhpCsFixer\Doctrine\Annotation\Tokens',
- 'PhpCsFixer\FileReader',
- 'PhpCsFixer\FileRemoval',
- 'PhpCsFixer\Fixer\Operator\AlignDoubleArrowFixerHelper',
- 'PhpCsFixer\Fixer\Operator\AlignEqualsFixerHelper',
- 'PhpCsFixer\Fixer\Phpdoc\GeneralPhpdocAnnotationRemoveFixer',
- 'PhpCsFixer\Runner\FileCachingLintingIterator',
- 'PhpCsFixer\Runner\FileLintingIterator',
- 'PhpCsFixer\StdinFileInfo',
- 'PhpCsFixer\Tokenizer\Transformers',
- );
- public function testThatClassesWithoutTestsVarIsProper()
- {
- $unknownClasses = array_filter(self::$classesWithoutTests, function ($class) { return !class_exists($class); });
- $this->assertSame(array(), $unknownClasses);
- }
- /**
- * @param string $className
- *
- * @dataProvider provideSrcConcreteClassCases
- */
- public function testThatSrcClassHaveTestClass($className)
- {
- $testClassName = str_replace('PhpCsFixer', 'PhpCsFixer\\Tests', $className).'Test';
- if (in_array($className, self::$classesWithoutTests, true)) {
- $this->assertFalse(class_exists($testClassName), sprintf('Class "%s" already has tests, so it should be removed from "%s::$classesWithoutTests".', $className, __CLASS__));
- $this->markTestIncomplete(sprintf('Class "%s" has no tests yet, please help and add it.', $className));
- }
- $this->assertTrue(class_exists($testClassName), sprintf('Expected test class "%s" for "%s" not found.', $testClassName, $className));
- $this->assertTrue(is_subclass_of($testClassName, '\PhpCsFixer\Tests\TestCase'), sprintf('Expected test class "%s" to be a subclass of "\PhpCsFixer\Tests\TestCase".', $testClassName));
- }
- /**
- * @param string $className
- *
- * @dataProvider provideSrcClassesNotAbuseInterfacesCases
- * @requires PHP 5.4
- */
- public function testThatSrcClassesNotAbuseInterfaces($className)
- {
- // HHVM knows better which interfaces you implements
- // https://github.com/facebook/hhvm/issues/5890
- if (defined('HHVM_VERSION') && interface_exists('Stringish')) {
- $this->markTestSkipped('Skipped as HHVM violate inheritance tree with `Stringish` interface.');
- }
- $rc = new \ReflectionClass($className);
- $allowedMethods = array_map(
- function (\ReflectionClass $interface) {
- return $this->getPublicMethodNames($interface);
- },
- $rc->getInterfaces()
- );
- if (count($allowedMethods)) {
- $allowedMethods = array_unique(call_user_func_array('array_merge', $allowedMethods));
- }
- $allowedMethods[] = '__construct';
- $allowedMethods[] = '__destruct';
- $allowedMethods[] = '__wakeup';
- $exceptionMethods = array(
- 'configure', // due to AbstractFixer::configure
- 'getConfigurationDefinition', // due to AbstractFixer::getConfigurationDefinition
- 'getDefaultConfiguration', // due to AbstractFixer::getDefaultConfiguration
- 'setWhitespacesConfig', // due to AbstractFixer::setWhitespacesConfig
- );
- // @TODO: should be removed at 3.0
- $exceptionMethodsPerClass = array(
- 'PhpCsFixer\Config' => array('create'),
- 'PhpCsFixer\Fixer\FunctionNotation\MethodArgumentSpaceFixer' => array('fixSpace'),
- );
- $definedMethods = $this->getPublicMethodNames($rc);
- $extraMethods = array_diff(
- $definedMethods,
- $allowedMethods,
- $exceptionMethods,
- isset($exceptionMethodsPerClass[$className]) ? $exceptionMethodsPerClass[$className] : array()
- );
- sort($extraMethods);
- $this->assertEmpty(
- $extraMethods,
- sprintf(
- "Class '%s' should not have public methods that are not part of implemented interfaces.\nViolations:\n%s",
- $className,
- implode("\n", array_map(function ($item) {
- return " * ${item}";
- }, $extraMethods))
- )
- );
- }
- /**
- * @param string $className
- *
- * @dataProvider provideSrcClassCases
- */
- public function testThatSrcClassesNotExposeProperties($className)
- {
- $rc = new \ReflectionClass($className);
- if ('PhpCsFixer\Fixer\Alias\NoMixedEchoPrintFixer' === $className) {
- $this->markTestIncomplete('Public properties of fixer \'PhpCsFixer\Fixer\Alias\NoMixedEchoPrintFixer\' will be remove on 3.0.');
- }
- $this->assertEmpty(
- $rc->getProperties(\ReflectionProperty::IS_PUBLIC),
- sprintf('Class \'%s\' should not have public properties.', $className)
- );
- if ($rc->isFinal()) {
- return;
- }
- $allowedProps = array();
- $definedProps = $rc->getProperties(\ReflectionProperty::IS_PROTECTED);
- if (false !== $rc->getParentClass()) {
- $allowedProps = $rc->getParentClass()->getProperties(\ReflectionProperty::IS_PROTECTED);
- }
- $allowedProps = array_map(function (\ReflectionProperty $item) {
- return $item->getName();
- }, $allowedProps);
- $definedProps = array_map(function (\ReflectionProperty $item) {
- return $item->getName();
- }, $definedProps);
- $exceptionPropsPerClass = array(
- 'PhpCsFixer\AbstractPhpdocTypesFixer' => array('tags'),
- 'PhpCsFixer\AbstractAlignFixerHelper' => array('deepestLevel'),
- 'PhpCsFixer\AbstractFixer' => array('configuration', 'configurationDefinition', 'whitespacesConfig'),
- 'PhpCsFixer\AbstractProxyFixer' => array('proxyFixer'),
- 'PhpCsFixer\Test\AbstractFixerTestCase' => array('fixer', 'linter'),
- 'PhpCsFixer\Test\AbstractIntegrationTestCase' => array('linter'),
- );
- $extraProps = array_diff(
- $definedProps,
- $allowedProps,
- isset($exceptionPropsPerClass[$className]) ? $exceptionPropsPerClass[$className] : array()
- );
- sort($extraProps);
- $this->assertEmpty(
- $extraProps,
- sprintf(
- "Class '%s' should not have protected properties.\nViolations:\n%s",
- $className,
- implode("\n", array_map(function ($item) {
- return " * ${item}";
- }, $extraProps))
- )
- );
- }
- /**
- * @param string $className
- *
- * @dataProvider provideTestClassCases
- */
- public function testThatTestClassesAreAbstractOrFinal($className)
- {
- $rc = new \ReflectionClass($className);
- $this->assertTrue(
- $rc->isInterface() || // due to hhvm only, @TODO remove me whem hhvm support is dropped
- $rc->isAbstract() || $rc->isFinal(),
- sprintf('Test class %s should be abstract or final.', $className)
- );
- }
- /**
- * @param string $className
- *
- * @dataProvider provideTestClassCases
- */
- public function testThatTestClassesAreInternal($className)
- {
- $rc = new \ReflectionClass($className);
- $doc = new DocBlock($rc->getDocComment());
- $this->assertNotEmpty(
- $doc->getAnnotationsOfType('internal'),
- sprintf('Test class %s should have internal annotation.', $className)
- );
- }
- /**
- * @dataProvider provideDataProviderMethodNameCases
- *
- * @param string $testClassName
- * @param string $dataProviderMethodName
- */
- public function testThatDataProvidersAreCorrectlyNamed($testClassName, $dataProviderMethodName)
- {
- $this->assertRegExp('/^provide[A-Z]\S+Cases$/', $dataProviderMethodName, sprintf(
- 'Data provider in "%s" with name "%s" is not correctly named.',
- $testClassName,
- $dataProviderMethodName
- ));
- }
- public function provideSrcClassCases()
- {
- return array_map(
- function ($item) {
- return array($item);
- },
- $this->getSrcClasses()
- );
- }
- public function provideSrcClassesNotAbuseInterfacesCases()
- {
- return array_map(
- function ($item) {
- return array($item);
- },
- array_filter($this->getSrcClasses(), function ($className) {
- $rc = new \ReflectionClass($className);
- $doc = false !== $rc->getDocComment()
- ? new DocBlock($rc->getDocComment())
- : null;
- if (
- $rc->isInterface()
- || ($doc && count($doc->getAnnotationsOfType('internal')))
- || 0 === count($rc->getInterfaces())
- || in_array($className, array(
- 'PhpCsFixer\Finder',
- 'PhpCsFixer\Test\AbstractFixerTestCase',
- 'PhpCsFixer\Test\AbstractIntegrationTestCase',
- 'PhpCsFixer\Tokenizer\Tokens',
- ), true)
- ) {
- return false;
- }
- return true;
- })
- );
- }
- public function provideSrcConcreteClassCases()
- {
- return array_map(
- function ($item) { return array($item); },
- array_filter(
- $this->getSrcClasses(),
- function ($className) {
- $rc = new \ReflectionClass($className);
- return !$rc->isAbstract() && !$rc->isInterface();
- }
- )
- );
- }
- public function provideTestClassCases()
- {
- return array_map(
- function ($item) {
- return array($item);
- },
- $this->getTestClasses()
- );
- }
- public function provideDataProviderMethodNameCases()
- {
- if (extension_loaded('xdebug') && false === getenv('CI')) {
- $this->markTestSkipped('Data provider too slow when Xdebug is loaded.');
- }
- $data = array();
- $testClassNames = $this->getTestClasses();
- foreach ($testClassNames as $testClassName) {
- $dataProviderMethodNames = array();
- $tokens = Tokens::fromCode(file_get_contents(
- str_replace('\\', DIRECTORY_SEPARATOR, preg_replace('#^PhpCsFixer\\\Tests#', 'tests', $testClassName)).'.php'
- ));
- foreach ($tokens as $token) {
- if ($token->isGivenKind(T_DOC_COMMENT)) {
- $docBlock = new DocBlock($token->getContent());
- $dataProviderAnnotations = $docBlock->getAnnotationsOfType('dataProvider');
- foreach ($dataProviderAnnotations as $dataProviderAnnotation) {
- if (1 === preg_match('/@dataProvider\s+(?P<methodName>\w+)/', $dataProviderAnnotation->getContent(), $matches)) {
- $dataProviderMethodNames[] = $matches['methodName'];
- }
- }
- }
- }
- $dataProviderMethodNames = array_unique($dataProviderMethodNames);
- foreach ($dataProviderMethodNames as $dataProviderMethodName) {
- $data[] = array(
- $testClassName,
- $dataProviderMethodName,
- );
- }
- }
- return $data;
- }
- private function getSrcClasses()
- {
- static $classes;
- if (null !== $classes) {
- return $classes;
- }
- $finder = Finder::create()
- ->files()
- ->name('*.php')
- ->in(__DIR__.'/../../src')
- ->exclude(array(
- 'Resources',
- ))
- ;
- $classes = array_map(
- function (SplFileInfo $file) {
- return sprintf(
- '%s\\%s%s%s',
- 'PhpCsFixer',
- strtr($file->getRelativePath(), DIRECTORY_SEPARATOR, '\\'),
- $file->getRelativePath() ? '\\' : '',
- $file->getBasename('.'.$file->getExtension())
- );
- },
- iterator_to_array($finder, false)
- );
- sort($classes);
- return $classes;
- }
- private function getTestClasses()
- {
- static $classes;
- if (null !== $classes) {
- return $classes;
- }
- $finder = Finder::create()
- ->files()
- ->name('*.php')
- ->in(__DIR__.'/..')
- ->exclude(array(
- 'Fixtures',
- ))
- ;
- $classes = array_map(
- function (SplFileInfo $file) {
- return sprintf(
- 'PhpCsFixer\\Tests\\%s%s%s',
- strtr($file->getRelativePath(), DIRECTORY_SEPARATOR, '\\'),
- $file->getRelativePath() ? '\\' : '',
- $file->getBasename('.'.$file->getExtension())
- );
- },
- iterator_to_array($finder, false)
- );
- sort($classes);
- return $classes;
- }
- /**
- * @param \ReflectionClass $rc
- *
- * @return string[]
- */
- private function getPublicMethodNames(\ReflectionClass $rc)
- {
- return array_map(
- function (\ReflectionMethod $rm) {
- return $rm->getName();
- },
- $rc->getMethods(\ReflectionMethod::IS_PUBLIC)
- );
- }
- }
|