Skip to content
Permalink
Browse files

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
  • Loading branch information
weirdan authored and muglug committed Nov 30, 2019
1 parent 4b597d4 commit 9027bc6190e1663d39332fe85e0ccfe87b36fe7b
@@ -29,11 +29,7 @@
*/
class PsalmEndToEndTest extends TestCase
{
/** @var string */
private $psalm = __DIR__ . '/../../psalm';
/** @var string */
private $psalter = __DIR__ . '/../../psalter';
use PsalmRunnerTrait;
/** @var string */
private static $tmpDir;
@@ -71,12 +67,12 @@ public function setUp(): 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
{
$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
@@ -91,23 +87,23 @@ public function testAlter(): void
$this->assertStringContainsString(
'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
{
$this->runPsalmInit();
(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
{
$this->runPsalmInit();
$result = $this->runPsalm([], true);
$result = $this->runPsalm([], self::$tmpDir, true);
$this->assertStringContainsString('InvalidReturnType', $result['STDOUT']);
$this->assertStringContainsString('InvalidReturnStatement', $result['STDOUT']);
$this->assertStringContainsString('2 errors', $result['STDOUT']);
@@ -130,45 +126,12 @@ public function testLegacyConfigWithoutresolveFromConfigFile(): void
$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}
*/
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
@@ -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(),
];
}
}
@@ -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/');
}
}
@@ -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);
});
@@ -0,0 +1 @@
<?php
@@ -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>

0 comments on commit 9027bc6

Please sign in to comment.
You can’t perform that action at this time.