1
0
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:
Bruce Weirdan 2019-11-30 07:09:07 +02:00 committed by Matthew Brown
parent 4b597d4ef9
commit 9027bc6190
6 changed files with 97 additions and 45 deletions

View File

@ -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

View 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(),
];
}
}

View 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/');
}
}

View 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);
});

View File

@ -0,0 +1 @@
<?php

View 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>