Skip to content
Permalink
Browse files

Add DataProviderAnalyzer (#188)

  • Loading branch information
kubawerlos committed Nov 15, 2019
1 parent e8622fb commit cdffa45131f0b15c8679178e08ed4e89c82a60a1
@@ -8,7 +8,7 @@

[![Build status](https://img.shields.io/travis/kubawerlos/php-cs-fixer-custom-fixers/master.svg)](https://travis-ci.org/kubawerlos/php-cs-fixer-custom-fixers)
[![Code coverage](https://img.shields.io/coveralls/github/kubawerlos/php-cs-fixer-custom-fixers/master.svg)](https://coveralls.io/github/kubawerlos/php-cs-fixer-custom-fixers?branch=master)
![Tests](https://img.shields.io/badge/tests-1460-brightgreen.svg)
![Tests](https://img.shields.io/badge/tests-1478-brightgreen.svg)
[![Mutation testing badge](https://badge.stryker-mutator.io/github.com/kubawerlos/php-cs-fixer-custom-fixers/master)](https://stryker-mutator.github.io)
[![Psalm type coverage](https://shepherd.dev/github/kubawerlos/php-cs-fixer-custom-fixers/coverage.svg)](https://shepherd.dev/github/kubawerlos/php-cs-fixer-custom-fixers)

@@ -25,6 +25,7 @@
<PossiblyNullOperand errorLevel='suppress' />
<PossiblyUnusedMethod>
<errorLevel type='suppress'>
<file name='./src/Analyzer/Analysis/DataProviderAnalysis.php' />
<file name='./src/Analyzer/Analysis/SwitchAnalysis.php' />
<file name='./src/Fixer/DeprecatingFixerInterface.php' />
</errorLevel>
@@ -0,0 +1,48 @@
<?php

declare(strict_types = 1);

namespace PhpCsFixerCustomFixers\Analyzer\Analysis;

/**
* @internal
*/
final class DataProviderAnalysis
{
/** @var string */
private $name;

/** @var int */
private $nameIndex;

/** @var int[] */
private $usageIndices;

/**
* @param int[] $usageIndices
*/
public function __construct(string $name, int $nameIndex, array $usageIndices)
{
$this->name = $name;
$this->nameIndex = $nameIndex;
$this->usageIndices = $usageIndices;
}

public function getName(): string
{
return $this->name;
}

public function getNameIndex(): int
{
return $this->nameIndex;
}

/**
* @return int[]
*/
public function getUsageIndices(): array
{
return $this->usageIndices;
}
}
@@ -0,0 +1,85 @@
<?php

declare(strict_types = 1);

namespace PhpCsFixerCustomFixers\Analyzer;

use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixerCustomFixers\Analyzer\Analysis\DataProviderAnalysis;

/**
* @internal
*/
final class DataProviderAnalyzer
{
/**
* @return DataProviderAnalysis[]
*/
public function getDataProviders(Tokens $tokens, int $startIndex, int $endIndex): array
{
$methods = $this->getMethods($tokens, $startIndex, $endIndex);

$dataProviders = [];
foreach ($methods as $methodIndex) {
/** @var int $docCommentIndex */
$docCommentIndex = $tokens->getTokenNotOfKindSibling(
$methodIndex,
-1,
[[T_ABSTRACT], [T_COMMENT], [T_FINAL], [T_FUNCTION], [T_PRIVATE], [T_PROTECTED], [T_PUBLIC], [T_STATIC], [T_WHITESPACE]]
);

if (!$tokens[$docCommentIndex]->isGivenKind(T_DOC_COMMENT)) {
continue;
}

Preg::matchAll('/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/', $tokens[$docCommentIndex]->getContent(), $matches);

/** @var string[] $matches */
$matches = $matches[1];

foreach ($matches as $dataProviderName) {
$dataProviders[$dataProviderName][] = $docCommentIndex;
}
}

$dataProviderAnalyses = [];
foreach ($dataProviders as $dataProviderName => $dataProviderUsages) {
if (!isset($methods[$dataProviderName])) {
continue;
}
$dataProviderAnalyses[$methods[$dataProviderName]] = new DataProviderAnalysis(
$dataProviderName,
$methods[$dataProviderName],
$dataProviderUsages
);
}

\ksort($dataProviderAnalyses);

return \array_values($dataProviderAnalyses);
}

/**
* @return array<string, int>
*/
private function getMethods(Tokens $tokens, int $startIndex, int $endIndex): array
{
$functions = [];
for ($index = $startIndex; $index < $endIndex; $index++) {
if (!$tokens[$index]->isGivenKind(T_FUNCTION)) {
continue;
}

/** @var int $functionNameIndex */
$functionNameIndex = $tokens->getNextNonWhitespace($index);
if (!$tokens[$functionNameIndex]->isGivenKind(T_STRING)) {
continue;
}

$functions[$tokens[$functionNameIndex]->getContent()] = $functionNameIndex;
}

return $functions;
}
}
@@ -0,0 +1,34 @@
<?php

declare(strict_types = 1);

namespace Tests\Analyzer\Analysis;

use PhpCsFixerCustomFixers\Analyzer\Analysis\DataProviderAnalysis;
use PHPUnit\Framework\TestCase;

/**
* @internal
*
* @covers \PhpCsFixerCustomFixers\Analyzer\Analysis\DataProviderAnalysis
*/
final class DataProviderAnalysisTest extends TestCase
{
public function testGetName(): void
{
$analysis = new DataProviderAnalysis('Foo', 1, [2, 3]);
static::assertSame('Foo', $analysis->getName());
}

public function testGetNameIndex(): void
{
$analysis = new DataProviderAnalysis('Foo', 1, [2, 3]);
static::assertSame(1, $analysis->getNameIndex());
}

public function testGetUsageIndices(): void
{
$analysis = new DataProviderAnalysis('Foo', 1, [2, 3]);
static::assertSame([2, 3], $analysis->getUsageIndices());
}
}
@@ -0,0 +1,132 @@
<?php

declare(strict_types = 1);

namespace Tests\Analyzer;

use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixerCustomFixers\Analyzer\Analysis\DataProviderAnalysis;
use PhpCsFixerCustomFixers\Analyzer\DataProviderAnalyzer;
use PHPUnit\Framework\TestCase;

/**
* @internal
*
* @covers \PhpCsFixerCustomFixers\Analyzer\DataProviderAnalyzer
*/
final class DataProviderAnalyzerTest extends TestCase
{
/**
* @dataProvider provideGettingDataProvidersCases
*/
public function testGettingDataProviders(array $expected, string $code, int $startIndex = 0, ?int $endIndex = null): void
{
$tokens = Tokens::fromCode($code);
if ($endIndex === null) {
$endIndex = $tokens->count() - 1;
}
$analyzer = new DataProviderAnalyzer();

static::assertSame(\serialize($expected), \serialize($analyzer->getDataProviders($tokens, $startIndex, $endIndex)));
}

public static function provideGettingDataProvidersCases(): iterable
{
yield 'single data provider' => [
[new DataProviderAnalysis('provider', 28, [11])],
'<?php class FooTest extends TestCase {
/**
* @dataProvider provider
*/
public function testFoo() {}
public function provider() {}
}',
];

yield 'single static data provider' => [
[new DataProviderAnalysis('provider', 30, [11])],
'<?php class FooTest extends TestCase {
/**
* @dataProvider provider
*/
public function testFoo() {}
public static function provider() {}
}',
];

yield 'multiple data provider' => [
[
new DataProviderAnalysis('provider1', 28, [11]),
new DataProviderAnalysis('provider2', 39, [11]),
new DataProviderAnalysis('provider3', 50, [11]),
],
'<?php class FooTest extends TestCase {
/**
* @dataProvider provider1
* @dataProvider provider2
* @dataProvider provider3
*/
public function testFoo() {}
public function provider1() {}
public function provider2() {}
public function provider3() {}
}',
];

foreach (['abstract', 'final', 'private', 'protected', 'static', '/* private */'] as $modifier) {
yield \sprintf('test function with %s modifier', $modifier) => [
[
new DataProviderAnalysis('provider1', 54, [37]),
new DataProviderAnalysis('provider2', 65, [11]),
new DataProviderAnalysis('provider3', 76, [24]),
],
\sprintf('<?php class FooTest extends TestCase {
/** @dataProvider provider2 */
public function testFoo1() {}
/** @dataProvider provider3 */
%s function testFoo2() {}
/** @dataProvider provider1 */
public function testFoo3() {}
public function provider1() {}
public function provider2() {}
public function provider3() {}
}', $modifier),
];
}

yield 'not existing data provider used' => [
[],
'<?php class FooTest extends TestCase {
/**
* @dataProvider provider
*/
public function testFoo() {}
}',
];

yield 'ignore anonymous function' => [
[
new DataProviderAnalysis('provider2', 93, [65]),
],
'<?php class FooTest extends TestCase {
public function testFoo0() {}
/**
* @dataProvider provider0
*/
public function testFoo1()
{
/**
* @dataProvider provider1
*/
$f = function ($x, $y) { return $x + $y; };
}
/**
* @dataProvider provider2
*/
public function testFoo2() {}
public function provider1() {}
public function provider2() {}
}',
];
}
}
@@ -33,9 +33,9 @@ public function testDataProviderName(string $dataProviderName, string $className
*/
public function testDataProviderReturnType(string $dataProviderName, string $className): void
{
$reflection = new \ReflectionMethod($className, $dataProviderName);
$reflectionMethod = new \ReflectionMethod($className, $dataProviderName);

static::assertSame('iterable', $reflection->getReturnType()->getName());
static::assertSame('iterable', $reflectionMethod->getReturnType()->getName());
}

public function provideDataProviderCases(): iterable

0 comments on commit cdffa45

Please sign in to comment.
You can’t perform that action at this time.