mirror of
https://github.com/danog/psalm-plugin-phpunit.git
synced 2024-11-30 04:29:08 +01:00
Merge pull request #10 from weirdan/7.5-features
Added PHPUnit 7.5 assertions
This commit is contained in:
commit
74215dc546
@ -9,6 +9,7 @@ install:
|
||||
- if [[ "$DEPS" = 'high' ]]; then travis_retry composer $DEFAULT_COMPOSER_FLAGS update; fi
|
||||
- if [[ "$DEPS" = 'low' ]]; then travis_retry composer $DEFAULT_COMPOSER_FLAGS --prefer-lowest --prefer-stable update; fi
|
||||
- if [[ "$DEPS" = 'stable' ]]; then travis_retry composer $DEFAULT_COMPOSER_FLAGS --prefer-stable update; fi
|
||||
- ./vendor/bin/psalm --version
|
||||
|
||||
script: composer check
|
||||
|
||||
|
18
Plugin.php
18
Plugin.php
@ -1,6 +1,9 @@
|
||||
<?php
|
||||
namespace Psalm\PhpUnitPlugin;
|
||||
|
||||
use Composer\Semver\Comparator;
|
||||
use Composer\Semver\VersionParser;
|
||||
use Muglug\PackageVersions\Versions;
|
||||
use SimpleXMLElement;
|
||||
use Psalm\Plugin\PluginEntryPointInterface;
|
||||
use Psalm\Plugin\RegistrationInterface;
|
||||
@ -11,9 +14,24 @@ class Plugin implements PluginEntryPointInterface
|
||||
public function __invoke(RegistrationInterface $psalm, SimpleXMLElement $config = null)
|
||||
{
|
||||
$psalm->addStubFile(__DIR__ . '/stubs/Assert.php');
|
||||
if ($this->packageVersionIs('phpunit/phpunit', '>=', '7.5')) {
|
||||
$psalm->addStubFile(__DIR__ . '/stubs/Assert_75.php');
|
||||
}
|
||||
$psalm->addStubFile(__DIR__ . '/stubs/TestCase.php');
|
||||
$psalm->addStubFile(__DIR__ . '/stubs/MockBuilder.php');
|
||||
$psalm->addStubFile(__DIR__ . '/stubs/InvocationMocker.php');
|
||||
$psalm->addStubFile(__DIR__ . '/stubs/Prophecy.php');
|
||||
}
|
||||
|
||||
private function packageVersionIs(string $package, string $op, string $ref): bool
|
||||
{
|
||||
$currentVersion = (string) Versions::getShortVersion($package);
|
||||
|
||||
$parser = new VersionParser();
|
||||
|
||||
$currentVersion = $parser->normalize($currentVersion);
|
||||
$ref = $parser->normalize($ref);
|
||||
|
||||
return Comparator::compare($currentVersion, $op, $ref);
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,9 @@
|
||||
],
|
||||
"require": {
|
||||
"phpunit/phpunit": "^5.0 || ^6.0 || ^7.0",
|
||||
"vimeo/psalm": "^3.0 || dev-master"
|
||||
"vimeo/psalm": "^3.0 || dev-master",
|
||||
"composer/semver": "^1.4",
|
||||
"muglug/package-versions-56": "^1.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"squizlabs/php_codesniffer": "^3.3.1",
|
||||
|
138
stubs/Assert_75.php
Normal file
138
stubs/Assert_75.php
Normal file
@ -0,0 +1,138 @@
|
||||
<?php // phpcs:ignoreFile
|
||||
namespace PHPUnit\Framework;
|
||||
|
||||
abstract class Assert
|
||||
{
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert array $actual
|
||||
*/
|
||||
public static function assertIsArray($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert bool $actual
|
||||
*/
|
||||
public static function assertIsBool($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert float $actual
|
||||
*/
|
||||
public static function assertIsFloat($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert int $actual
|
||||
*/
|
||||
public static function assertIsInt($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert numeric $actual
|
||||
*/
|
||||
public static function assertIsNumeric($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert object $actual
|
||||
*/
|
||||
public static function assertIsObject($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert resource $actual
|
||||
*/
|
||||
public static function assertIsResource($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert string $actual
|
||||
*/
|
||||
public static function assertIsString($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert scalar $actual
|
||||
*/
|
||||
public static function assertIsScalar($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert callable $actual
|
||||
*/
|
||||
public static function assertIsCallable($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert iterable $actual
|
||||
*/
|
||||
public static function assertIsIterable($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert !array $actual
|
||||
*/
|
||||
public static function assertIsNotArray($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert !bool $actual
|
||||
*/
|
||||
public static function assertIsNotBool($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert !float $actual
|
||||
*/
|
||||
public static function assertIsNotFloat($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert !int $actual
|
||||
*/
|
||||
public static function assertIsNotInt($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert !numeric $actual
|
||||
*/
|
||||
public static function assertIsNotNumeric($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert !object $actual
|
||||
*/
|
||||
public static function assertIsNotObject($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert !resource $actual
|
||||
*/
|
||||
public static function assertIsNotResource($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert !string $actual
|
||||
*/
|
||||
public static function assertIsNotString($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert !scalar $actual
|
||||
*/
|
||||
public static function assertIsNotScalar($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert !callable $actual
|
||||
*/
|
||||
public static function assertIsNotCallable($actual, string $message = ''): void {}
|
||||
|
||||
/**
|
||||
* @param mixed $actual
|
||||
* @psalm-assert !iterable $actual
|
||||
*/
|
||||
public static function assertIsNotIterable($actual, string $message = ''): void {}
|
||||
|
||||
}
|
@ -1,10 +1,46 @@
|
||||
<?php
|
||||
namespace Psalm\PhpUnitPlugin\Tests\Helper;
|
||||
|
||||
use Codeception\Exception\Skip;
|
||||
use Codeception\Exception\TestRuntimeException;
|
||||
use Composer\Semver\Comparator;
|
||||
use Composer\Semver\VersionParser;
|
||||
use Muglug\PackageVersions\Versions;
|
||||
|
||||
// here you can define custom actions
|
||||
// all public methods declared in helper class will be available in $I
|
||||
|
||||
class Acceptance extends \Codeception\Module
|
||||
{
|
||||
/** @var array<string,string */
|
||||
const VERSION_OPERATORS = [
|
||||
'newer than' => '>',
|
||||
'older than' => '<',
|
||||
];
|
||||
|
||||
/**
|
||||
* @Given /I have PHPUnit (newer than|older than) "([0-9.]+)" \(because of "([^"]+)"\)/
|
||||
*/
|
||||
public function havePHPUnitOfACertainVersionRangeBecauseOf(string $operator, string $version, string $reason): void
|
||||
{
|
||||
if (!isset(self::VERSION_OPERATORS[$operator])) {
|
||||
throw new TestRuntimeException("Unknown operator: $operator");
|
||||
}
|
||||
|
||||
$op = (string) self::VERSION_OPERATORS[$operator];
|
||||
|
||||
$currentVersion = (string) Versions::getShortVersion('phpunit/phpunit');
|
||||
$this->debug(sprintf("Current version: %s", $currentVersion));
|
||||
|
||||
$parser = new VersionParser();
|
||||
|
||||
$currentVersion = $parser->normalize($currentVersion);
|
||||
$version = $parser->normalize($version);
|
||||
|
||||
$result = Comparator::compare($currentVersion, $op, $version);
|
||||
$this->debug("Comparing $currentVersion $op $version => $result");
|
||||
if (!$result) {
|
||||
throw new Skip("This scenario requires PHPUnit $op $version because of $reason");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ Feature: Assert
|
||||
|
||||
"""
|
||||
|
||||
Scenario: Asserting instanceof
|
||||
Scenario: Assert::assertInstanceOf()
|
||||
Given I have the following code
|
||||
"""
|
||||
function f(): \Exception {
|
||||
|
279
tests/acceptance/Assert75.feature
Normal file
279
tests/acceptance/Assert75.feature
Normal file
@ -0,0 +1,279 @@
|
||||
Feature: Assert (PHPUnit 7.5+)
|
||||
In order to use PHPUnit safely
|
||||
As a Psalm user
|
||||
I need Psalm to typecheck asserts
|
||||
|
||||
Background:
|
||||
Given I have the following code preamble
|
||||
"""
|
||||
<?php
|
||||
namespace NS;
|
||||
use PHPUnit\Framework\Assert;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @psalm-suppress InvalidReturnType
|
||||
*/
|
||||
function mixed() {}
|
||||
|
||||
"""
|
||||
And I have PHPUnit newer than "7.4.99999" (because of "new features in 7.5")
|
||||
|
||||
Scenario: Assert::assertIsArray()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @psalm-suppress MixedAssignment */
|
||||
$a = mixed();
|
||||
Assert::assertIsArray($a);
|
||||
array_pop($a);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsBool()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @psalm-suppress MixedAssignment */
|
||||
$b = mixed();
|
||||
|
||||
Assert::assertIsBool($b);
|
||||
microtime($b);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsFloat()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @psalm-suppress MixedAssignment */
|
||||
$f = mixed();
|
||||
|
||||
Assert::assertIsFloat($f);
|
||||
atan($f);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsInt()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @psalm-suppress MixedAssignment */
|
||||
$i = mixed();
|
||||
|
||||
Assert::assertIsInt($i);
|
||||
substr('foo', $i);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsNumeric()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @psalm-suppress MixedAssignment */
|
||||
$n = mixed();
|
||||
|
||||
Assert::assertIsNumeric($n);
|
||||
$n + $n;
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsObject()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @psalm-suppress MixedAssignment */
|
||||
$o = mixed();
|
||||
|
||||
Assert::assertIsObject($o);
|
||||
$o->foo = 1;
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsResource()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @psalm-suppress MixedAssignment */
|
||||
$r = mixed();
|
||||
|
||||
Assert::assertIsResource($r);
|
||||
get_resource_type($r);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsString()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @psalm-suppress MixedAssignment */
|
||||
$s = mixed();
|
||||
|
||||
Assert::assertIsString($s);
|
||||
strlen($s);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsScalar()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @psalm-suppress MixedAssignment */
|
||||
$s = mixed();
|
||||
|
||||
Assert::assertIsScalar($s); // int|string|float|bool
|
||||
// all of the following should cause errors
|
||||
if (is_array($s)) {}
|
||||
if (is_resource($s)) {}
|
||||
if (is_object($s)) {}
|
||||
if (is_null($s)) {}
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see these errors
|
||||
| Type | Message |
|
||||
| DocblockTypeContradiction | Cannot resolve types for $s - docblock-defined type scalar does not contain array<%, mixed> |
|
||||
| DocblockTypeContradiction | Cannot resolve types for $s - docblock-defined type scalar does not contain resource |
|
||||
| DocblockTypeContradiction | Found a contradiction with a docblock-defined type when evaluating $s and trying to reconcile type 'scalar' to object |
|
||||
| DocblockTypeContradiction | Cannot resolve types for $s - docblock-defined type scalar does not contain null |
|
||||
|
||||
Scenario: Assert::assertIsCallable()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @psalm-suppress MixedAssignment */
|
||||
$s = mixed();
|
||||
|
||||
Assert::assertIsCallable($s);
|
||||
\Closure::fromCallable($s);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsIterable()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @return iterable */
|
||||
function () {
|
||||
/** @psalm-suppress MixedAssignment */
|
||||
$s = mixed();
|
||||
|
||||
Assert::assertIsIterable($s);
|
||||
return $s;
|
||||
};
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsNotArray()
|
||||
Given I have the following code
|
||||
"""
|
||||
$i = rand(0, 1) ? 1 : [1];
|
||||
Assert::assertIsNotArray($i);
|
||||
substr("foo", $i);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsNotBool()
|
||||
Given I have the following code
|
||||
"""
|
||||
$i = rand(0, 1) ? 1 : true;
|
||||
Assert::assertIsNotBool($i);
|
||||
substr("foo", $i);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsNotFloat()
|
||||
Given I have the following code
|
||||
"""
|
||||
$i = rand(0, 1) ? 1 : 0.1;
|
||||
Assert::assertIsNotFloat($i);
|
||||
substr("foo", $i);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsNotInt()
|
||||
Given I have the following code
|
||||
"""
|
||||
$a = rand(0, 1) ? 1 : [1];
|
||||
Assert::assertIsNotInt($a);
|
||||
array_pop($a);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsNotNumeric()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @return numeric|array */
|
||||
function f() { return rand(0,1) ? 1 : [1]; }
|
||||
$a = f();
|
||||
Assert::assertIsNotNumeric($a);
|
||||
array_pop($a);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsNotObject()
|
||||
Given I have the following code
|
||||
"""
|
||||
$a = rand(0, 1) ? ((object)[]) : [1];
|
||||
Assert::assertIsNotObject($a);
|
||||
array_pop($a);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsNotResource()
|
||||
Given I have the following code
|
||||
"""
|
||||
$a = rand(0, 1) ? STDIN : [1];
|
||||
Assert::assertIsNotResource($a);
|
||||
array_pop($a);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsNotString()
|
||||
Given I have the following code
|
||||
"""
|
||||
$a = rand(0, 1) ? "foo" : [1];
|
||||
Assert::assertIsNotString($a);
|
||||
array_pop($a);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsNotScalar()
|
||||
Given I have the following code
|
||||
"""
|
||||
$a = rand(0, 1) ? "foo" : [1];
|
||||
Assert::assertIsNotScalar($a);
|
||||
array_pop($a);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsNotCallable()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @return callable|float */
|
||||
function f() { return rand(0,1) ? 'f' : 1.1; }
|
||||
$a = f();
|
||||
Assert::assertIsNotCallable($a);
|
||||
atan($a);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
||||
Scenario: Assert::assertIsNotIterable()
|
||||
Given I have the following code
|
||||
"""
|
||||
/** @var string|iterable $s */
|
||||
$s = rand(0, 1) ? "foo" : [1];
|
||||
Assert::assertIsNotIterable($s);
|
||||
strlen($s);
|
||||
"""
|
||||
When I run Psalm
|
||||
Then I see no errors
|
||||
|
Loading…
Reference in New Issue
Block a user