Просмотр исходного кода

PhpdocOrderByValueFixer - Introduction

Andreas Möller 5 лет назад
Родитель
Сommit
8c0a85f87b

+ 13 - 2
README.rst

@@ -1452,9 +1452,10 @@ Choose from the list of available rules:
     ``'newest'``
     ``'newest'``
   - ``use_class_const`` (``bool``): use ::class notation; defaults to ``true``
   - ``use_class_const`` (``bool``): use ::class notation; defaults to ``true``
 
 
-* **php_unit_ordered_covers** [@PhpCsFixer]
+* **php_unit_ordered_covers**
 
 
-  Order ``@covers`` annotation of PHPUnit tests.
+  Order ``@covers`` annotation of PHPUnit tests. DEPRECATED: use
+  ``phpdoc_order_by_value`` instead.
 
 
 * **php_unit_set_up_tear_down_visibility** [@PhpCsFixer:risky]
 * **php_unit_set_up_tear_down_visibility** [@PhpCsFixer:risky]
 
 
@@ -1600,6 +1601,16 @@ Choose from the list of available rules:
   Annotations in PHPDoc should be ordered so that ``@param`` annotations
   Annotations in PHPDoc should be ordered so that ``@param`` annotations
   come first, then ``@throws`` annotations, then ``@return`` annotations.
   come first, then ``@throws`` annotations, then ``@return`` annotations.
 
 
+* **phpdoc_order_by_value** [@PhpCsFixer]
+
+  Order phpdoc tags by value.
+
+  Configuration options:
+
+  - ``annotations`` (a subset of ``['author', 'covers', 'coversNothing',
+    'dataProvider', 'depends', 'group', 'internal', 'requires', 'uses']``):
+    list of annotations to order, e.g. ``["covers"]``; defaults to ``['covers']``
+
 * **phpdoc_return_self_reference** [@Symfony, @PhpCsFixer]
 * **phpdoc_return_self_reference** [@Symfony, @PhpCsFixer]
 
 
   The type of ``@return`` annotations of methods returning a reference to
   The type of ``@return`` annotations of methods returning a reference to

+ 20 - 55
src/Fixer/PhpUnit/PhpUnitOrderedCoversFixer.php

@@ -12,18 +12,20 @@
 
 
 namespace PhpCsFixer\Fixer\PhpUnit;
 namespace PhpCsFixer\Fixer\PhpUnit;
 
 
-use PhpCsFixer\AbstractFixer;
-use PhpCsFixer\DocBlock\DocBlock;
+use PhpCsFixer\AbstractProxyFixer;
+use PhpCsFixer\Fixer\DeprecatedFixerInterface;
+use PhpCsFixer\Fixer\Phpdoc\PhpdocOrderByValueFixer;
 use PhpCsFixer\FixerDefinition\CodeSample;
 use PhpCsFixer\FixerDefinition\CodeSample;
 use PhpCsFixer\FixerDefinition\FixerDefinition;
 use PhpCsFixer\FixerDefinition\FixerDefinition;
-use PhpCsFixer\Preg;
-use PhpCsFixer\Tokenizer\Token;
-use PhpCsFixer\Tokenizer\Tokens;
 
 
 /**
 /**
+ * @deprecated since 2.16, replaced by PhpUnitOrderedAnnotationsFixer
+ *
+ * @todo To be removed at 3.0
+ *
  * @author Filippo Tessarotto <zoeslam@gmail.com>
  * @author Filippo Tessarotto <zoeslam@gmail.com>
  */
  */
-final class PhpUnitOrderedCoversFixer extends AbstractFixer
+final class PhpUnitOrderedCoversFixer extends AbstractProxyFixer implements DeprecatedFixerInterface
 {
 {
     /**
     /**
      * {@inheritdoc}
      * {@inheritdoc}
@@ -47,60 +49,23 @@ final class MyTest extends \PHPUnit_Framework_TestCase
         );
         );
     }
     }
 
 
-    /**
-     * {@inheritdoc}
-     */
-    public function getPriority()
+    public function getSuccessorsNames()
     {
     {
-        // should be run after PhpUnitFqcnAnnotationFixer
-        return -10;
+        return array_keys($this->proxyFixers);
     }
     }
 
 
-    /**
-     * {@inheritdoc}
-     */
-    public function isCandidate(Tokens $tokens)
+    protected function createProxyFixers()
     {
     {
-        return $tokens->isAllTokenKindsFound([T_CLASS, T_DOC_COMMENT]);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
-    {
-        for ($index = $tokens->count() - 1; $index > 0; --$index) {
-            if (!$tokens[$index]->isGivenKind(T_DOC_COMMENT) || 0 === Preg::match('/@covers\s.+@covers\s/s', $tokens[$index]->getContent())) {
-                continue;
-            }
-
-            $docBlock = new DocBlock($tokens[$index]->getContent());
-            $covers = $docBlock->getAnnotationsOfType('covers');
-
-            $coversMap = [];
-            foreach ($covers as $annotation) {
-                $rawContent = $annotation->getContent();
-
-                $comparableContent = Preg::replace('/\*\s*@covers\s+(.+)/', '\1', strtolower(trim($rawContent)));
-                $coversMap[$comparableContent] = $rawContent;
-            }
-            $orderedCoversMap = $coversMap;
-            ksort($orderedCoversMap, SORT_STRING);
-            if ($orderedCoversMap === $coversMap) {
-                continue;
-            }
+        $fixer = new PhpdocOrderByValueFixer();
 
 
-            $lines = $docBlock->getLines();
-            foreach (array_reverse($covers) as $annotation) {
-                array_splice(
-                    $lines,
-                    $annotation->getStart(),
-                    $annotation->getEnd() - $annotation->getStart() + 1,
-                    array_pop($orderedCoversMap)
-                );
-            }
+        $fixer->configure([
+            'annotations' => [
+                'covers',
+            ],
+        ]);
 
 
-            $tokens[$index] = new Token([T_DOC_COMMENT, implode('', $lines)]);
-        }
+        return [
+            $fixer,
+        ];
     }
     }
 }
 }

+ 186 - 0
src/Fixer/Phpdoc/PhpdocOrderByValueFixer.php

@@ -0,0 +1,186 @@
+<?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\Fixer\Phpdoc;
+
+use PhpCsFixer\AbstractFixer;
+use PhpCsFixer\DocBlock\DocBlock;
+use PhpCsFixer\Fixer\ConfigurationDefinitionFixerInterface;
+use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
+use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
+use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
+use PhpCsFixer\FixerDefinition\CodeSample;
+use PhpCsFixer\FixerDefinition\FixerDefinition;
+use PhpCsFixer\Preg;
+use PhpCsFixer\Tokenizer\Token;
+use PhpCsFixer\Tokenizer\Tokens;
+
+/**
+ * @author Filippo Tessarotto <zoeslam@gmail.com>
+ * @author Andreas Möller <am@localheinz.com>
+ */
+final class PhpdocOrderByValueFixer extends AbstractFixer implements ConfigurationDefinitionFixerInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefinition()
+    {
+        return new FixerDefinition(
+            'Order phpdoc tags by value.',
+            [
+                new CodeSample(
+                    '<?php
+/**
+ * @covers Foo
+ * @covers Bar
+ */
+final class MyTest extends \PHPUnit_Framework_TestCase
+{}
+'
+                ),
+                new CodeSample(
+                    '<?php
+/**
+ * @author Bob
+ * @author Alice
+ */
+final class MyTest extends \PHPUnit_Framework_TestCase
+{}
+',
+                    [
+                        'annotations' => [
+                            'author',
+                        ],
+                    ]
+                ),
+            ]
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPriority()
+    {
+        // should be run after PhpUnitFqcnAnnotationFixer
+        return -10;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isCandidate(Tokens $tokens)
+    {
+        return $tokens->isAllTokenKindsFound([T_CLASS, T_DOC_COMMENT]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
+    {
+        if ([] === $this->configuration['annotations']) {
+            return;
+        }
+
+        for ($index = $tokens->count() - 1; $index > 0; --$index) {
+            foreach ($this->configuration['annotations'] as $type) {
+                $findPattern = sprintf(
+                    '/@%s\s.+@%s\s/s',
+                    $type,
+                    $type
+                );
+
+                if (
+                    !$tokens[$index]->isGivenKind(T_DOC_COMMENT)
+                    || 0 === Preg::match($findPattern, $tokens[$index]->getContent())
+                ) {
+                    continue;
+                }
+
+                $docBlock = new DocBlock($tokens[$index]->getContent());
+
+                $annotations = $docBlock->getAnnotationsOfType($type);
+
+                $annotationMap = [];
+
+                $replacePattern = sprintf(
+                    '/\*\s*@%s\s+(.+)/',
+                    $type
+                );
+
+                foreach ($annotations as $annotation) {
+                    $rawContent = $annotation->getContent();
+
+                    $comparableContent = Preg::replace(
+                        $replacePattern,
+                        '\1',
+                        strtolower(trim($rawContent))
+                    );
+
+                    $annotationMap[$comparableContent] = $rawContent;
+                }
+
+                $orderedAnnotationMap = $annotationMap;
+
+                ksort($orderedAnnotationMap, SORT_STRING);
+
+                if ($orderedAnnotationMap === $annotationMap) {
+                    continue;
+                }
+
+                $lines = $docBlock->getLines();
+
+                foreach (array_reverse($annotations) as $annotation) {
+                    array_splice(
+                        $lines,
+                        $annotation->getStart(),
+                        $annotation->getEnd() - $annotation->getStart() + 1,
+                        array_pop($orderedAnnotationMap)
+                    );
+                }
+
+                $tokens[$index] = new Token([T_DOC_COMMENT, implode('', $lines)]);
+            }
+        }
+    }
+
+    protected function createConfigurationDefinition()
+    {
+        $allowedValues = [
+            'author',
+            'covers',
+            'coversNothing',
+            'dataProvider',
+            'depends',
+            'group',
+            'internal',
+            'requires',
+            'uses',
+        ];
+
+        return new FixerConfigurationResolver([
+            (new FixerOptionBuilder('annotations', 'List of annotations to order, e.g. `["covers"]`.'))
+                ->setAllowedTypes([
+                    'array',
+                ])
+                ->setAllowedValues([
+                    new AllowedValueSubset($allowedValues),
+                ])
+                ->setDefault([
+                    'covers',
+                ])
+                ->getOption(),
+        ]);
+    }
+}

+ 1 - 1
src/RuleSet.php

@@ -246,11 +246,11 @@ final class RuleSet implements RuleSetInterface
             'ordered_class_elements' => true,
             'ordered_class_elements' => true,
             'php_unit_internal_class' => true,
             'php_unit_internal_class' => true,
             'php_unit_method_casing' => true,
             'php_unit_method_casing' => true,
-            'php_unit_ordered_covers' => true,
             'php_unit_test_class_requires_covers' => true,
             'php_unit_test_class_requires_covers' => true,
             'phpdoc_add_missing_param_annotation' => true,
             'phpdoc_add_missing_param_annotation' => true,
             'phpdoc_no_empty_return' => true,
             'phpdoc_no_empty_return' => true,
             'phpdoc_order' => true,
             'phpdoc_order' => true,
+            'phpdoc_order_by_value' => true,
             'phpdoc_types_order' => true,
             'phpdoc_types_order' => true,
             'phpdoc_var_annotation_correct_order' => true,
             'phpdoc_var_annotation_correct_order' => true,
             'protected_to_private' => true,
             'protected_to_private' => true,

+ 1323 - 0
tests/Fixer/Phpdoc/PhpdocOrderByValueFixerTest.php

@@ -0,0 +1,1323 @@
+<?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\Fixer\Phpdoc;
+
+use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
+use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
+
+/**
+ * @author Filippo Tessarotto <zoeslam@gmail.com>
+ * @author Andreas Möller <am@localheinz.com>
+ *
+ * @internal
+ *
+ * @covers \PhpCsFixer\Fixer\Phpdoc\PhpdocOrderByValueFixer
+ */
+final class PhpdocOrderByValueFixerTest extends AbstractFixerTestCase
+{
+    /**
+     * @dataProvider provideInvalidAnnotationCases
+     *
+     * @param mixed $annotation
+     */
+    public function testConfigureRejectsInvalidControlStatement($annotation)
+    {
+        $this->expectException(InvalidFixerConfigurationException::class);
+
+        $this->fixer->configure([
+            'annotations' => [
+                $annotation,
+            ],
+        ]);
+    }
+
+    /**
+     * @return array
+     */
+    public function provideInvalidAnnotationCases()
+    {
+        return [
+            'null' => [null],
+            'false' => [false],
+            'true' => [true],
+            'int' => [0],
+            'float' => [3.14],
+            'array' => [[]],
+            'object' => [new \stdClass()],
+            'unknown' => ['foo'],
+        ];
+    }
+
+    /**
+     * @param string      $expected
+     * @param null|string $input
+     *
+     * @dataProvider provideFixWithCoversCases
+     */
+    public function testFixWithDefaultConfiguration($expected, $input = null)
+    {
+        $this->doTest($expected, $input);
+    }
+
+    /**
+     * @param string      $expected
+     * @param null|string $input
+     *
+     * @dataProvider provideFixWithAuthorCases
+     */
+    public function testFixWithAuthor($expected, $input = null)
+    {
+        $this->fixer->configure([
+            'annotations' => [
+                'author',
+            ],
+        ]);
+
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixWithAuthorCases()
+    {
+        return [
+            'skip on 1 or 0 occurrences' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase {
+                        /**
+                         * @author Bob
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe() {}
+
+                        /**
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe2() {}
+                    }
+                ',
+            ],
+            'base case' => [
+                '<?php
+                    /**
+                     * @author Alice
+                     * @author Bob
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @author Bob
+                     * @author Alice
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'preserve positions if other docblock parts are present' => [
+                '<?php
+                    /**
+                     * Comment 1
+                     * @author Alice
+                     * Comment 3
+                     * @author Bob
+                     * Comment 2
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * Comment 1
+                     * @author Bob
+                     * Comment 2
+                     * @author Alice
+                     * Comment 3
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'case-insensitive' => [
+                '<?php
+                    /**
+                     * @author Alice
+                     * @author chris
+                     * @author Daniel
+                     * @author Erna
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @author Alice
+                     * @author Erna
+                     * @author chris
+                     * @author Daniel
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'data provider' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @author Alice
+                         * @dataProvider provide
+                         * @author Bob
+                         */
+                        public function testMe() {}
+                    }
+                ',
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @author Bob
+                         * @dataProvider provide
+                         * @author Alice
+                         */
+                        public function testMe() {}
+                    }
+                ',
+            ],
+        ];
+    }
+
+    /**
+     * @param string      $expected
+     * @param null|string $input
+     *
+     * @dataProvider provideFixWithCoversCases
+     */
+    public function testFixWithCovers($expected, $input = null)
+    {
+        $this->fixer->configure([
+            'annotations' => [
+                'covers',
+            ],
+        ]);
+
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixWithCoversCases()
+    {
+        return [
+            'skip on 1 or 0 occurrences' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase {
+                        /**
+                         * @covers Foo
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe() {}
+
+                        /**
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe2() {}
+                    }
+                ',
+            ],
+            'base case' => [
+                '<?php
+                    /**
+                     * @covers Bar
+                     * @covers Foo
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @covers Foo
+                     * @covers Bar
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'preserve positions if other docblock parts are present' => [
+                '<?php
+                    /**
+                     * Comment 1
+                     * @covers Bar
+                     * Comment 3
+                     * @covers Foo
+                     * Comment 2
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * Comment 1
+                     * @covers Foo
+                     * Comment 2
+                     * @covers Bar
+                     * Comment 3
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'case-insensitive' => [
+                '<?php
+                    /**
+                     * @covers A
+                     * @covers c
+                     * @covers D
+                     * @covers E
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @covers A
+                     * @covers E
+                     * @covers c
+                     * @covers D
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'data provider' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @covers Bar
+                         * @dataProvider provide
+                         * @covers Foo
+                         */
+                        public function testMe() {}
+                    }
+                ',
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @covers Foo
+                         * @dataProvider provide
+                         * @covers Bar
+                         */
+                        public function testMe() {}
+                    }
+                ',
+            ],
+        ];
+    }
+
+    /**
+     * @param string      $expected
+     * @param null|string $input
+     *
+     * @dataProvider provideFixWithCoversNothingCases
+     */
+    public function testFixWithCoversNothing($expected, $input = null)
+    {
+        $this->fixer->configure([
+            'annotations' => [
+                'coversNothing',
+            ],
+        ]);
+
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixWithCoversNothingCases()
+    {
+        return [
+            'skip on 1 or 0 occurrences' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase {
+                        /**
+                         * @coversNothing Foo
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe() {}
+
+                        /**
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe2() {}
+                    }
+                ',
+            ],
+            'base case' => [
+                '<?php
+                    /**
+                     * @coversNothing Bar
+                     * @coversNothing Foo
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @coversNothing Foo
+                     * @coversNothing Bar
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'preserve positions if other docblock parts are present' => [
+                '<?php
+                    /**
+                     * Comment 1
+                     * @coversNothing Bar
+                     * Comment 3
+                     * @coversNothing Foo
+                     * Comment 2
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * Comment 1
+                     * @coversNothing Foo
+                     * Comment 2
+                     * @coversNothing Bar
+                     * Comment 3
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'case-insensitive' => [
+                '<?php
+                    /**
+                     * @coversNothing A
+                     * @coversNothing c
+                     * @coversNothing D
+                     * @coversNothing E
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @coversNothing A
+                     * @coversNothing E
+                     * @coversNothing c
+                     * @coversNothing D
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'data provider' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @coversNothing Bar
+                         * @dataProvider provide
+                         * @coversNothing Foo
+                         */
+                        public function testMe() {}
+                    }
+                ',
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @coversNothing Foo
+                         * @dataProvider provide
+                         * @coversNothing Bar
+                         */
+                        public function testMe() {}
+                    }
+                ',
+            ],
+        ];
+    }
+
+    /**
+     * @param string      $expected
+     * @param null|string $input
+     *
+     * @dataProvider provideFixWithDataProviderCases
+     */
+    public function testFixWithDataProvider($expected, $input = null)
+    {
+        $this->fixer->configure([
+            'annotations' => [
+                'dataProvider',
+            ],
+        ]);
+
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixWithDataProviderCases()
+    {
+        return [
+            'skip on 1 or 0 occurrences' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase {
+                        /**
+                         * @dataProvider Foo::provideData()
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe() {}
+
+                        /**
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe2() {}
+                    }
+                ',
+            ],
+            'base case' => [
+                '<?php
+                    /**
+                     * @dataProvider Bar::provideData()
+                     * @dataProvider Foo::provideData()
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @dataProvider Foo::provideData()
+                     * @dataProvider Bar::provideData()
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'preserve positions if other docblock parts are present' => [
+                '<?php
+                    /**
+                     * Comment 1
+                     * @dataProvider Bar::provideData()
+                     * Comment 3
+                     * @dataProvider Foo::provideData()
+                     * Comment 2
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * Comment 1
+                     * @dataProvider Foo::provideData()
+                     * Comment 2
+                     * @dataProvider Bar::provideData()
+                     * Comment 3
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'case-insensitive' => [
+                '<?php
+                    /**
+                     * @dataProvider A::provideData()
+                     * @dataProvider c::provideData()
+                     * @dataProvider D::provideData()
+                     * @dataProvider E::provideData()
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @dataProvider A::provideData()
+                     * @dataProvider E::provideData()
+                     * @dataProvider c::provideData()
+                     * @dataProvider D::provideData()
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'data provider' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @dataProvider Bar::provideData()
+                         * @dataProvider dataProvider
+                         * @dataProvider Foo::provideData()
+                         */
+                        public function testMe() {}
+                    }
+                ',
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @dataProvider Foo::provideData()
+                         * @dataProvider dataProvider
+                         * @dataProvider Bar::provideData()
+                         */
+                        public function testMe() {}
+                    }
+                ',
+            ],
+        ];
+    }
+
+    /**
+     * @param string      $expected
+     * @param null|string $input
+     *
+     * @dataProvider provideFixWithDependsCases
+     */
+    public function testFixWithDepends($expected, $input = null)
+    {
+        $this->fixer->configure([
+            'annotations' => [
+                'depends',
+            ],
+        ]);
+
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixWithDependsCases()
+    {
+        return [
+            'skip on 1 or 0 occurrences' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase {
+                        /**
+                         * @depends testFoo
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe() {}
+
+                        /**
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe2() {}
+                    }
+                ',
+            ],
+            'base case' => [
+                '<?php
+                    /**
+                     * @depends testBar
+                     * @depends testFoo
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @depends testFoo
+                     * @depends testBar
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'preserve positions if other docblock parts are present' => [
+                '<?php
+                    /**
+                     * Comment 1
+                     * @depends testBar
+                     * Comment 3
+                     * @depends testFoo
+                     * Comment 2
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * Comment 1
+                     * @depends testFoo
+                     * Comment 2
+                     * @depends testBar
+                     * Comment 3
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'case-insensitive' => [
+                '<?php
+                    /**
+                     * @depends testA
+                     * @depends testc
+                     * @depends testD
+                     * @depends testE
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @depends testA
+                     * @depends testE
+                     * @depends testc
+                     * @depends testD
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'data provider' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @depends testBar
+                         * @dataProvider provide
+                         * @depends testFoo
+                         */
+                        public function testMe() {}
+                    }
+                ',
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @depends testFoo
+                         * @dataProvider provide
+                         * @depends testBar
+                         */
+                        public function testMe() {}
+                    }
+                ',
+            ],
+        ];
+    }
+
+    /**
+     * @param string      $expected
+     * @param null|string $input
+     *
+     * @dataProvider provideFixWithGroupCases
+     */
+    public function testFixWithGroup($expected, $input = null)
+    {
+        $this->fixer->configure([
+            'annotations' => [
+                'group',
+            ],
+        ]);
+
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixWithGroupCases()
+    {
+        return [
+            'skip on 1 or 0 occurrences' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase {
+                        /**
+                         * @group slow
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe() {}
+
+                        /**
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe2() {}
+                    }
+                ',
+            ],
+            'base case' => [
+                '<?php
+                    /**
+                     * @group fast
+                     * @group slow
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @group slow
+                     * @group fast
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'preserve positions if other docblock parts are present' => [
+                '<?php
+                    /**
+                     * Comment 1
+                     * @group fast
+                     * Comment 3
+                     * @group slow
+                     * Comment 2
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * Comment 1
+                     * @group slow
+                     * Comment 2
+                     * @group fast
+                     * Comment 3
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'case-insensitive' => [
+                '<?php
+                    /**
+                     * @group A
+                     * @group c
+                     * @group D
+                     * @group E
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @group A
+                     * @group E
+                     * @group c
+                     * @group D
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'data provider' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @group fast
+                         * @dataProvider provide
+                         * @group slow
+                         */
+                        public function testMe() {}
+                    }
+                ',
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @group slow
+                         * @dataProvider provide
+                         * @group fast
+                         */
+                        public function testMe() {}
+                    }
+                ',
+            ],
+        ];
+    }
+
+    /**
+     * @param string      $expected
+     * @param null|string $input
+     *
+     * @dataProvider provideFixWithInternalCases
+     */
+    public function testFixWithInternal($expected, $input = null)
+    {
+        $this->fixer->configure([
+            'annotations' => [
+                'internal',
+            ],
+        ]);
+
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixWithInternalCases()
+    {
+        return [
+            'skip on 1 or 0 occurrences' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase {
+                        /**
+                         * @internal Foo
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe() {}
+
+                        /**
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe2() {}
+                    }
+                ',
+            ],
+            'base case' => [
+                '<?php
+                    /**
+                     * @internal Bar
+                     * @internal Foo
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @internal Foo
+                     * @internal Bar
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'preserve positions if other docblock parts are present' => [
+                '<?php
+                    /**
+                     * Comment 1
+                     * @internal Bar
+                     * Comment 3
+                     * @internal Foo
+                     * Comment 2
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * Comment 1
+                     * @internal Foo
+                     * Comment 2
+                     * @internal Bar
+                     * Comment 3
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'case-insensitive' => [
+                '<?php
+                    /**
+                     * @internal A
+                     * @internal c
+                     * @internal D
+                     * @internal E
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @internal A
+                     * @internal E
+                     * @internal c
+                     * @internal D
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'data provider' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @internal Bar
+                         * @dataProvider provide
+                         * @internal Foo
+                         */
+                        public function testMe() {}
+                    }
+                ',
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @internal Foo
+                         * @dataProvider provide
+                         * @internal Bar
+                         */
+                        public function testMe() {}
+                    }
+                ',
+            ],
+        ];
+    }
+
+    /**
+     * @param string      $expected
+     * @param null|string $input
+     *
+     * @dataProvider provideFixWithRequiresCases
+     */
+    public function testFixWithRequires($expected, $input = null)
+    {
+        $this->fixer->configure([
+            'annotations' => [
+                'requires',
+            ],
+        ]);
+
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixWithRequiresCases()
+    {
+        return [
+            'skip on 1 or 0 occurrences' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase {
+                        /**
+                         * @requires function json_decode
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe() {}
+
+                        /**
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe2() {}
+                    }
+                ',
+            ],
+            'base case' => [
+                '<?php
+                    /**
+                     * @requires extension redis
+                     * @requires function json_decode
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @requires function json_decode
+                     * @requires extension redis
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'preserve positions if other docblock parts are present' => [
+                '<?php
+                    /**
+                     * Comment 1
+                     * @requires extension redis
+                     * Comment 3
+                     * @requires function json_decode
+                     * Comment 2
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * Comment 1
+                     * @requires function json_decode
+                     * Comment 2
+                     * @requires extension redis
+                     * Comment 3
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'case-insensitive' => [
+                '<?php
+                    /**
+                     * @requires extension redis
+                     * @requires function json_decode
+                     * @requires OS Linux
+                     * @requires PHP 7.2
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @requires PHP 7.2
+                     * @requires function json_decode
+                     * @requires extension redis
+                     * @requires OS Linux
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'data provider' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @requires extension redis
+                         * @dataProvider provide
+                         * @requires function json_decode
+                         */
+                        public function testMe() {}
+                    }
+                ',
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @requires function json_decode
+                         * @dataProvider provide
+                         * @requires extension redis
+                         */
+                        public function testMe() {}
+                    }
+                ',
+            ],
+        ];
+    }
+
+    /**
+     * @param string      $expected
+     * @param null|string $input
+     *
+     * @dataProvider provideFixWithUsesCases
+     */
+    public function testFixWithUses($expected, $input = null)
+    {
+        $this->fixer->configure([
+            'annotations' => [
+                'uses',
+            ],
+        ]);
+
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixWithUsesCases()
+    {
+        return [
+            'skip on 1 or 0 occurrences' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase {
+                        /**
+                         * @uses Foo
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe() {}
+
+                        /**
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe2() {}
+                    }
+                ',
+            ],
+            'base case' => [
+                '<?php
+                    /**
+                     * @uses Bar
+                     * @uses Foo
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @uses Foo
+                     * @uses Bar
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'preserve positions if other docblock parts are present' => [
+                '<?php
+                    /**
+                     * Comment 1
+                     * @uses Bar
+                     * Comment 3
+                     * @uses Foo
+                     * Comment 2
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * Comment 1
+                     * @uses Foo
+                     * Comment 2
+                     * @uses Bar
+                     * Comment 3
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'case-insensitive' => [
+                '<?php
+                    /**
+                     * @uses A
+                     * @uses c
+                     * @uses D
+                     * @uses E
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @uses A
+                     * @uses E
+                     * @uses c
+                     * @uses D
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'data provider' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @uses Bar
+                         * @dataProvider provide
+                         * @uses Foo
+                         */
+                        public function testMe() {}
+                    }
+                ',
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @uses Foo
+                         * @dataProvider provide
+                         * @uses Bar
+                         */
+                        public function testMe() {}
+                    }
+                ',
+            ],
+        ];
+    }
+
+    /**
+     * @param string      $expected
+     * @param null|string $input
+     *
+     * @dataProvider provideFixWithMultipleConfiguredAnnotationsCases
+     */
+    public function testFixWithMultipleConfiguredAnnotations($expected, $input = null)
+    {
+        $this->fixer->configure([
+            'annotations' => [
+                'covers',
+                'uses',
+            ],
+        ]);
+
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixWithMultipleConfiguredAnnotationsCases()
+    {
+        return [
+            'skip on 1 or 0 occurrences' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase {
+                        /**
+                         * @uses Foo
+                         * @covers Baz
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe() {}
+
+                        /**
+                         * @params bool $bool
+                         * @return void
+                         */
+                        public function testMe2() {}
+                    }
+                ',
+            ],
+            'base case' => [
+                '<?php
+                    /**
+                     * @uses Bar
+                     * @uses Foo
+                     * @covers Baz
+                     * @covers Qux
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @uses Foo
+                     * @uses Bar
+                     * @covers Qux
+                     * @covers Baz
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'preserve positions if other docblock parts are present' => [
+                '<?php
+                    /**
+                     * Comment 1
+                     * @uses Bar
+                     * Comment 3
+                     * @uses Foo
+                     * Comment 2
+                     * @covers Baz
+                     * Comment 5
+                     * @covers Qux
+                     * Comment 4
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * Comment 1
+                     * @uses Foo
+                     * Comment 2
+                     * @uses Bar
+                     * Comment 3
+                     * @covers Qux
+                     * Comment 4
+                     * @covers Baz
+                     * Comment 5
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'case-insensitive' => [
+                '<?php
+                    /**
+                     * @uses A
+                     * @uses c
+                     * @covers D
+                     * @covers e
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+                '<?php
+                    /**
+                     * @uses c
+                     * @uses A
+                     * @covers e
+                     * @covers D
+                     */
+                    class FooTest extends \PHPUnit_Framework_TestCase {}
+                ',
+            ],
+            'data provider' => [
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @uses Bar
+                         * @dataProvider provideOne
+                         * @uses Foo
+                         * @dataProvider provideTwo
+                         * @covers Baz
+                         * @dataProvider provideThree
+                         * @covers Qux
+                         */
+                        public function testMe() {}
+                    }
+                ',
+                '<?php
+                    class FooTest extends \PHPUnit_Framework_TestCase
+                    {
+                        /**
+                         * @uses Foo
+                         * @dataProvider provideOne
+                         * @uses Bar
+                         * @dataProvider provideTwo
+                         * @covers Qux
+                         * @dataProvider provideThree
+                         * @covers Baz
+                         */
+                        public function testMe() {}
+                    }
+                ',
+            ],
+        ];
+    }
+}