Tweak dead code detection on TestCase descendants

- Don't report TestCase descendants as unused
- Don't report test methods as unused
- Don't report providers referenced by test methods as unused

Refs psalm/phpunit-psalm-plugin#13

/cc @SignpostMarv
This commit is contained in:
Bruce Weirdan 2019-02-20 13:19:11 +02:00
parent 330b3a1ef6
commit 4fe3c88aca
No known key found for this signature in database
GPG Key ID: CFC3AAB181751B0D
2 changed files with 143 additions and 2 deletions

View File

@ -53,7 +53,17 @@ class TestCaseHandler implements AfterClassLikeVisitInterface, AfterClassLikeAna
return null; return null;
} }
/** @var MethodStorage $method_storage */ // add a fake reference to test class to prevent it from being marked as unused
// it would have been easier to add a suppression, but that's only possible
// since 3.0.17 (vimeo/psalm#1353)
//
// This should always pass, we're calling it for the side-effect
// of adding self-reference
if (!$codebase->classOrInterfaceExists($class_storage->name, $class_storage->location)) {
return null;
}
foreach ($class_storage->methods as $method_name => $method_storage) { foreach ($class_storage->methods as $method_name => $method_storage) {
if (!$method_storage->location) { if (!$method_storage->location) {
continue; continue;
@ -69,6 +79,13 @@ class TestCaseHandler implements AfterClassLikeVisitInterface, AfterClassLikeAna
$method_id = $class_storage->name . '::' . $method_storage->cased_name; $method_id = $class_storage->name . '::' . $method_storage->cased_name;
if (0 !== strpos($method_storage->cased_name, 'test')
&& !isset($specials['test'])) {
continue; // skip non-test methods
}
$method_storage->suppressed_issues[] = 'PossiblyUnusedMethod';
if (!isset($specials['dataProvider'])) { if (!isset($specials['dataProvider'])) {
continue; continue;
} }
@ -79,7 +96,8 @@ class TestCaseHandler implements AfterClassLikeVisitInterface, AfterClassLikeAna
$provider_docblock_location = clone $method_storage->location; $provider_docblock_location = clone $method_storage->location;
$provider_docblock_location->setCommentLine($line); $provider_docblock_location->setCommentLine($line);
if (!$codebase->methodExists($provider_method_id)) { // methodExists also can mark methods as used (weird, but handy)
if (!$codebase->methodExists($provider_method_id, $provider_docblock_location, $method_id)) {
IssueBuffer::accepts(new Issue\UndefinedMethod( IssueBuffer::accepts(new Issue\UndefinedMethod(
'Provider method ' . $provider_method_id . ' is not defined', 'Provider method ' . $provider_method_id . ' is not defined',
$provider_docblock_location, $provider_docblock_location,

View File

@ -41,6 +41,7 @@ Feature: TestCase
Then I see these errors Then I see these errors
| Type | Message | | Type | Message |
| InvalidArgument | Argument 1 of PHPUnit\Framework\TestCase::expectException expects class-string<Throwable>, NS\MyTestCase::class provided | | InvalidArgument | Argument 1 of PHPUnit\Framework\TestCase::expectException expects class-string<Throwable>, NS\MyTestCase::class provided |
And I see no other errors
Scenario: TestCase::expectException() accepts throwables Scenario: TestCase::expectException() accepts throwables
Given I have the following code Given I have the following code
@ -144,6 +145,7 @@ Feature: TestCase
Then I see these errors Then I see these errors
| Type | Message | | Type | Message |
| MissingConstructor | NS\MyTestCase has an uninitialized variable $this->i, but no constructor | | MissingConstructor | NS\MyTestCase has an uninitialized variable $this->i, but no constructor |
And I see no other errors
Scenario: Missing data provider is reported Scenario: Missing data provider is reported
Given I have the following code Given I have the following code
@ -192,6 +194,7 @@ Feature: TestCase
Then I see these errors Then I see these errors
| Type | Message | | Type | Message |
| InvalidReturnType | Providers must return iterable<int\|string, array<array-key, mixed>>, iterable<int, int> provided | | InvalidReturnType | Providers must return iterable<int\|string, array<array-key, mixed>>, iterable<int, int> provided |
And I see no other errors
Scenario: Valid iterable data provider is allowed Scenario: Valid iterable data provider is allowed
Given I have the following code Given I have the following code
@ -240,6 +243,7 @@ Feature: TestCase
Then I see these errors Then I see these errors
| Type | Message | | Type | Message |
| InvalidReturnType | Providers must return iterable<int\|string, array<array-key, mixed>>, Generator<int, int, mixed, void> provided | | InvalidReturnType | Providers must return iterable<int\|string, array<array-key, mixed>>, Generator<int, int, mixed, void> provided |
And I see no other errors
Scenario: Valid generator data provider is allowed Scenario: Valid generator data provider is allowed
Given I have the following code Given I have the following code
@ -288,6 +292,7 @@ Feature: TestCase
Then I see these errors Then I see these errors
| Type | Message | | Type | Message |
| InvalidReturnType | Providers must return iterable<int\|string, array<array-key, mixed>>, array<int, int> provided | | InvalidReturnType | Providers must return iterable<int\|string, array<array-key, mixed>>, array<int, int> provided |
And I see no other errors
Scenario: Valid array data provider is allowed Scenario: Valid array data provider is allowed
Given I have the following code Given I have the following code
@ -363,6 +368,7 @@ Feature: TestCase
Then I see these errors Then I see these errors
| Type | Message | | Type | Message |
| InvalidArgument | Argument 1 of NS\MyTestCase::testSomething expects int, string provided by NS\MyTestCase::provide():(iterable<string, array{0:string}>) | | InvalidArgument | Argument 1 of NS\MyTestCase::testSomething expects int, string provided by NS\MyTestCase::provide():(iterable<string, array{0:string}>) |
And I see no other errors
Scenario: Invalid dataset array is reported Scenario: Invalid dataset array is reported
Given I have the following code Given I have the following code
@ -388,6 +394,7 @@ Feature: TestCase
Then I see these errors Then I see these errors
| Type | Message | | Type | Message |
| PossiblyInvalidArgument | Argument 1 of NS\MyTestCase::testSomething expects int, string\|int provided by NS\MyTestCase::provide():(iterable<string, array<int, string\|int>>) | | PossiblyInvalidArgument | Argument 1 of NS\MyTestCase::testSomething expects int, string\|int provided by NS\MyTestCase::provide():(iterable<string, array<int, string\|int>>) |
And I see no other errors
Scenario: Shape dataset with missing params is reported Scenario: Shape dataset with missing params is reported
Given I have the following code Given I have the following code
@ -413,3 +420,119 @@ Feature: TestCase
Then I see these errors Then I see these errors
| Type | Message | | Type | Message |
| TooFewArguments | Too few arguments for NS\MyTestCase::testSomething - expecting 2 but saw 1 provided by NS\MyTestCase::provide():(iterable<string, array{0:int}>) | | TooFewArguments | Too few arguments for NS\MyTestCase::testSomething - expecting 2 but saw 1 provided by NS\MyTestCase::provide():(iterable<string, array{0:int}>) |
And I see no other errors
Scenario: Referenced providers are not marked as unused
Given I have the following code
"""
class MyTestCase extends TestCase
{
/** @return iterable<string,array{int}> */
public function provide() {
yield "data set name" => [1];
}
/**
* @return void
* @psalm-suppress UnusedMethod
* @dataProvider provide
*/
public function testSomething(int $int) {
$this->assertEquals(1, $int);
}
}
new MyTestCase;
"""
When I run Psalm with dead code detection
Then I see no errors
Scenario: Unreferenced providers are marked as unused
Given I have the following code
"""
class MyTestCase extends TestCase
{
/** @return iterable<string,array{int}> */
public function provide() {
yield "data set name" => [1];
}
/**
* @return void
* @psalm-suppress UnusedMethod
*/
public function testSomething(int $int) {
$this->assertEquals(1, $int);
}
}
new MyTestCase;
"""
When I run Psalm with dead code detection
Then I see these errors
| Type | Message |
| PossiblyUnusedMethod | Cannot find public calls to method NS\MyTestCase::provide |
And I see no other errors
Scenario: Test method are never marked as unused
Given I have the following code
"""
class MyTestCase extends TestCase
{
/**
* @return void
*/
public function testSomething(int $int) {
$this->assertEquals(1, $int);
}
/**
* @return void
* @test
*/
public function somethingElse(int $int) {
$this->assertEquals(1, $int);
}
}
new MyTestCase;
"""
When I run Psalm with dead code detection
Then I see no errors
Scenario: Unreferenced non-test methods are marked as unused
Given I have the following code
"""
class MyTestCase extends TestCase
{
/**
* @return void
*/
public function somethingElse(int $int) {
$this->assertEquals(1, $int);
}
}
new MyTestCase;
"""
When I run Psalm with dead code detection
Then I see these errors
| Type | Message |
| PossiblyUnusedMethod | Cannot find public calls to method NS\MyTestCase::somethingElse |
And I see no other errors
Scenario: Unreferenced TestCase descendants are never marked as unused
Given I have the following code
"""
class MyTestCase extends TestCase
{
}
"""
When I run Psalm with dead code detection
Then I see no errors
Scenario: Unreferenced non-test classes are marked as unused
Given I have the following code
"""
class UtilityClass
{
}
"""
When I run Psalm with dead code detection
Then I see these errors
| Type | Message |
| UnusedClass | Class NS\UtilityClass is never used |
And I see no other errors