mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Added SuicidalAutoloader test (#2399)
The idea behind this is that Psalm should not use project autoloader for its own things. So if we have a project with autoloader and no code, then any project autoloader hit means Psalm failed to load something itself. Right now it highlights several issues in CoreGenericClasses stub: - usage of `callback` instead of `callable` - `@property-read` not resolving template parameters
This commit is contained in:
parent
4b597d4ef9
commit
9027bc6190
@ -29,11 +29,7 @@ use function preg_replace;
|
|||||||
*/
|
*/
|
||||||
class PsalmEndToEndTest extends TestCase
|
class PsalmEndToEndTest extends TestCase
|
||||||
{
|
{
|
||||||
/** @var string */
|
use PsalmRunnerTrait;
|
||||||
private $psalm = __DIR__ . '/../../psalm';
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
private $psalter = __DIR__ . '/../../psalter';
|
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private static $tmpDir;
|
private static $tmpDir;
|
||||||
@ -71,12 +67,12 @@ class PsalmEndToEndTest extends TestCase
|
|||||||
|
|
||||||
public function testHelpReturnsMessage(): void
|
public function testHelpReturnsMessage(): void
|
||||||
{
|
{
|
||||||
$this->assertStringContainsString('Usage:', $this->runPsalm(['--help'])['STDOUT']);
|
$this->assertStringContainsString('Usage:', $this->runPsalm(['--help'], self::$tmpDir)['STDOUT']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testVersion(): void
|
public function testVersion(): void
|
||||||
{
|
{
|
||||||
$this->assertStringStartsWith('Psalm 3', $this->runPsalm(['--version'], false, false)['STDOUT']);
|
$this->assertStringStartsWith('Psalm 3', $this->runPsalm(['--version'], self::$tmpDir, false, false)['STDOUT']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInit(): void
|
public function testInit(): void
|
||||||
@ -91,23 +87,23 @@ class PsalmEndToEndTest extends TestCase
|
|||||||
|
|
||||||
$this->assertStringContainsString(
|
$this->assertStringContainsString(
|
||||||
'No errors found!',
|
'No errors found!',
|
||||||
$this->runPsalm(['--alter', '--issues=all'], false, true)['STDOUT']
|
$this->runPsalm(['--alter', '--issues=all'], self::$tmpDir, false, true)['STDOUT']
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertSame(0, $this->runPsalm([])['CODE']);
|
$this->assertSame(0, $this->runPsalm([], self::$tmpDir)['CODE']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPsalter(): void
|
public function testPsalter(): void
|
||||||
{
|
{
|
||||||
$this->runPsalmInit();
|
$this->runPsalmInit();
|
||||||
(new Process(['php', $this->psalter, '--alter', '--issues=InvalidReturnType'], self::$tmpDir))->mustRun();
|
(new Process(['php', $this->psalter, '--alter', '--issues=InvalidReturnType'], self::$tmpDir))->mustRun();
|
||||||
$this->assertSame(0, $this->runPsalm([])['CODE']);
|
$this->assertSame(0, $this->runPsalm([], self::$tmpDir)['CODE']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPsalm(): void
|
public function testPsalm(): void
|
||||||
{
|
{
|
||||||
$this->runPsalmInit();
|
$this->runPsalmInit();
|
||||||
$result = $this->runPsalm([], true);
|
$result = $this->runPsalm([], self::$tmpDir, true);
|
||||||
$this->assertStringContainsString('InvalidReturnType', $result['STDOUT']);
|
$this->assertStringContainsString('InvalidReturnType', $result['STDOUT']);
|
||||||
$this->assertStringContainsString('InvalidReturnStatement', $result['STDOUT']);
|
$this->assertStringContainsString('InvalidReturnStatement', $result['STDOUT']);
|
||||||
$this->assertStringContainsString('2 errors', $result['STDOUT']);
|
$this->assertStringContainsString('2 errors', $result['STDOUT']);
|
||||||
@ -130,45 +126,12 @@ class PsalmEndToEndTest extends TestCase
|
|||||||
$this->assertStringContainsString('InvalidReturnType', $process->getOutput());
|
$this->assertStringContainsString('InvalidReturnType', $process->getOutput());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string> $args
|
|
||||||
*
|
|
||||||
* @return array{STDOUT: string, STDERR: string, CODE: int|null}
|
|
||||||
*/
|
|
||||||
private function runPsalm(array $args, bool $shouldFail = false, bool $relyOnConfigDir = true): array
|
|
||||||
{
|
|
||||||
// As config files all contain `resolveFromConfigFile="true"` Psalm shouldn't need to be run from the same
|
|
||||||
// directory that the code being analysed exists in.
|
|
||||||
|
|
||||||
// Windows doesn't read shabangs, so to allow this to work on windows we run `php psalm` rather than just `psalm`.
|
|
||||||
|
|
||||||
if ($relyOnConfigDir) {
|
|
||||||
$process = new Process(array_merge(['php', $this->psalm, '-c=' . self::$tmpDir . '/psalm.xml'], $args), null);
|
|
||||||
} else {
|
|
||||||
$process = new Process(array_merge(['php', $this->psalm], $args), self::$tmpDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$shouldFail) {
|
|
||||||
$process->mustRun();
|
|
||||||
} else {
|
|
||||||
$process->run();
|
|
||||||
$this->assertGreaterThan(0, $process->getExitCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'STDOUT' => $process->getOutput(),
|
|
||||||
'STDERR' => $process->getErrorOutput(),
|
|
||||||
'CODE' => $process->getExitCode(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array{STDOUT: string, STDERR: string, CODE: int|null}
|
* @return array{STDOUT: string, STDERR: string, CODE: int|null}
|
||||||
*/
|
*/
|
||||||
private function runPsalmInit(): array
|
private function runPsalmInit(): array
|
||||||
{
|
{
|
||||||
return $this->runPsalm(['--init'], false, false);
|
return $this->runPsalm(['--init'], self::$tmpDir, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** from comment by itay at itgoldman dot com at
|
/** from comment by itay at itgoldman dot com at
|
||||||
|
54
tests/EndToEnd/PsalmRunnerTrait.php
Normal file
54
tests/EndToEnd/PsalmRunnerTrait.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psalm\Tests\EndToEnd;
|
||||||
|
|
||||||
|
use Symfony\Component\Process\Process;
|
||||||
|
|
||||||
|
use function array_merge;
|
||||||
|
|
||||||
|
trait PsalmRunnerTrait
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
private $psalm = __DIR__ . '/../../psalm';
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $psalter = __DIR__ . '/../../psalter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param list<string> $args
|
||||||
|
*
|
||||||
|
* @return array{STDOUT: string, STDERR: string, CODE: int|null}
|
||||||
|
*/
|
||||||
|
private function runPsalm(
|
||||||
|
array $args,
|
||||||
|
string $workingDir,
|
||||||
|
bool $shouldFail = false,
|
||||||
|
bool $relyOnConfigDir = true
|
||||||
|
): array {
|
||||||
|
// As config files all contain `resolveFromConfigFile="true"` Psalm
|
||||||
|
// shouldn't need to be run from the same directory that the code being
|
||||||
|
// analysed exists in.
|
||||||
|
|
||||||
|
// Windows doesn't read shabangs, so to allow this to work on windows
|
||||||
|
// we run `php psalm` rather than just `psalm`.
|
||||||
|
|
||||||
|
if ($relyOnConfigDir) {
|
||||||
|
$process = new Process(array_merge(['php', $this->psalm, '-c=' . $workingDir . '/psalm.xml'], $args), null);
|
||||||
|
} else {
|
||||||
|
$process = new Process(array_merge(['php', $this->psalm], $args), $workingDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$shouldFail) {
|
||||||
|
$process->mustRun();
|
||||||
|
} else {
|
||||||
|
$process->run();
|
||||||
|
$this->assertGreaterThan(0, $process->getExitCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'STDOUT' => $process->getOutput(),
|
||||||
|
'STDERR' => $process->getErrorOutput(),
|
||||||
|
'CODE' => $process->getExitCode(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
15
tests/EndToEnd/SuicidalAutoloaderTest.php
Normal file
15
tests/EndToEnd/SuicidalAutoloaderTest.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
namespace Psalm\Tests\EndToEnd;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Process\Process;
|
||||||
|
|
||||||
|
class SuicidalAutoloaderTest extends TestCase
|
||||||
|
{
|
||||||
|
use PsalmRunnerTrait;
|
||||||
|
|
||||||
|
public function testSucceedsWithEmptyFile(): void
|
||||||
|
{
|
||||||
|
$this->runPsalm([], __DIR__ . '/' . '../fixtures/SuicidalAutoloader/');
|
||||||
|
}
|
||||||
|
}
|
6
tests/fixtures/SuicidalAutoloader/autoloader.php
vendored
Normal file
6
tests/fixtures/SuicidalAutoloader/autoloader.php
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
spl_autoload_register(function (string $className) {
|
||||||
|
$ex = new RuntimeException('Attempted to load ' . $className);
|
||||||
|
echo $ex->__toString() . "\n\n" . $ex->getTraceAsString() . "\n\n";
|
||||||
|
exit(70);
|
||||||
|
});
|
1
tests/fixtures/SuicidalAutoloader/file.php
vendored
Normal file
1
tests/fixtures/SuicidalAutoloader/file.php
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?php
|
13
tests/fixtures/SuicidalAutoloader/psalm.xml
vendored
Normal file
13
tests/fixtures/SuicidalAutoloader/psalm.xml
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<psalm
|
||||||
|
totallyTyped="true"
|
||||||
|
autoloader="autoloader.php"
|
||||||
|
resolveFromConfigFile="true"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="https://getpsalm.org/schema/config"
|
||||||
|
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||||
|
>
|
||||||
|
<projectFiles>
|
||||||
|
<file name="file.php" />
|
||||||
|
</projectFiles>
|
||||||
|
</psalm>
|
Loading…
Reference in New Issue
Block a user