Skip to content

Commit

Permalink
Tweak dead code detection on TestCase descendants
Browse files Browse the repository at this point in the history
- 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#13

/cc @SignpostMarv
  • Loading branch information
weirdan committed Feb 20, 2019
1 parent 330b3a1 commit 4fe3c88
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 2 deletions.
22 changes: 20 additions & 2 deletions hooks/TestCaseHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,17 @@ public static function afterStatementAnalysis(
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) {
if (!$method_storage->location) {
continue;
Expand All @@ -69,6 +79,13 @@ public static function afterStatementAnalysis(

$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'])) {
continue;
}
Expand All @@ -79,7 +96,8 @@ public static function afterStatementAnalysis(
$provider_docblock_location = clone $method_storage->location;
$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(
'Provider method ' . $provider_method_id . ' is not defined',
$provider_docblock_location,
Expand Down
123 changes: 123 additions & 0 deletions tests/acceptance/TestCase.feature
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Feature: TestCase
Then I see these errors
| Type | Message |
| 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
Given I have the following code
Expand Down Expand Up @@ -144,6 +145,7 @@ Feature: TestCase
Then I see these errors
| Type | Message |
| MissingConstructor | NS\MyTestCase has an uninitialized variable $this->i, but no constructor |
And I see no other errors

Scenario: Missing data provider is reported
Given I have the following code
Expand Down Expand Up @@ -192,6 +194,7 @@ Feature: TestCase
Then I see these errors
| Type | Message |
| 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
Given I have the following code
Expand Down Expand Up @@ -240,6 +243,7 @@ Feature: TestCase
Then I see these errors
| Type | Message |
| 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
Given I have the following code
Expand Down Expand Up @@ -288,6 +292,7 @@ Feature: TestCase
Then I see these errors
| Type | Message |
| 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
Given I have the following code
Expand Down Expand Up @@ -363,6 +368,7 @@ Feature: TestCase
Then I see these errors
| Type | Message |
| 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
Given I have the following code
Expand All @@ -388,6 +394,7 @@ Feature: TestCase
Then I see these errors
| Type | Message |
| 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
Given I have the following code
Expand All @@ -413,3 +420,119 @@ Feature: TestCase
Then I see these errors
| 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}>) |
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

0 comments on commit 4fe3c88

Please sign in to comment.