2018-10-17 17:03:32 +02:00
|
|
|
<?php
|
|
|
|
namespace Psalm\Tests;
|
|
|
|
|
2019-02-24 19:48:57 +01:00
|
|
|
use Psalm\Codebase;
|
2018-11-06 03:57:36 +01:00
|
|
|
use Psalm\Internal\Analyzer\FileAnalyzer;
|
2018-10-17 17:03:32 +02:00
|
|
|
use Psalm\Config;
|
2019-02-24 19:48:57 +01:00
|
|
|
use Psalm\Plugin\Hook\AfterCodebasePopulatedInterface;
|
2018-11-12 16:57:05 +01:00
|
|
|
use Psalm\Tests\Internal\Provider;
|
2018-10-17 17:03:32 +02:00
|
|
|
|
2018-11-06 03:57:36 +01:00
|
|
|
class ProjectAnalyzerTest extends TestCase
|
2018-10-17 17:03:32 +02:00
|
|
|
{
|
|
|
|
/** @var TestConfig */
|
|
|
|
protected static $config;
|
|
|
|
|
2018-11-06 03:57:36 +01:00
|
|
|
/** @var \Psalm\Internal\Analyzer\ProjectAnalyzer */
|
2018-11-11 18:01:14 +01:00
|
|
|
protected $project_analyzer;
|
2018-10-17 17:03:32 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public static function setUpBeforeClass()
|
|
|
|
{
|
|
|
|
self::$config = new TestConfig();
|
|
|
|
|
|
|
|
if (!defined('PSALM_VERSION')) {
|
|
|
|
define('PSALM_VERSION', '2.0.0');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!defined('PHP_PARSER_VERSION')) {
|
|
|
|
define('PHP_PARSER_VERSION', '4.0.0');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setUp()
|
|
|
|
{
|
2018-11-06 03:57:36 +01:00
|
|
|
FileAnalyzer::clearCache();
|
2018-10-17 17:03:32 +02:00
|
|
|
$this->file_provider = new Provider\FakeFileProvider();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param Config $config
|
|
|
|
*
|
2018-11-06 03:57:36 +01:00
|
|
|
* @return \Psalm\Internal\Analyzer\ProjectAnalyzer
|
2018-10-17 17:03:32 +02:00
|
|
|
*/
|
2018-11-06 03:57:36 +01:00
|
|
|
private function getProjectAnalyzerWithConfig(Config $config)
|
2018-10-17 17:03:32 +02:00
|
|
|
{
|
2018-11-06 03:57:36 +01:00
|
|
|
return new \Psalm\Internal\Analyzer\ProjectAnalyzer(
|
2018-10-17 17:03:32 +02:00
|
|
|
$config,
|
2018-11-06 03:57:36 +01:00
|
|
|
new \Psalm\Internal\Provider\Providers(
|
2018-10-17 17:03:32 +02:00
|
|
|
$this->file_provider,
|
|
|
|
new Provider\ParserInstanceCacheProvider(),
|
|
|
|
new Provider\FileStorageInstanceCacheProvider(),
|
|
|
|
new Provider\ClassLikeStorageInstanceCacheProvider(),
|
|
|
|
new Provider\FakeFileReferenceCacheProvider()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function testCheck()
|
|
|
|
{
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
2018-10-17 17:03:32 +02:00
|
|
|
Config::loadFromXML(
|
|
|
|
(string)getcwd(),
|
|
|
|
'<?xml version="1.0"?>
|
|
|
|
<psalm>
|
|
|
|
<projectFiles>
|
|
|
|
<directory name="tests/DummyProject" />
|
|
|
|
</projectFiles>
|
|
|
|
</psalm>'
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
ob_start();
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->check('tests/DummyProject');
|
2018-10-17 17:03:32 +02:00
|
|
|
$output = ob_get_clean();
|
|
|
|
|
|
|
|
$this->assertSame('Scanning files...' . "\n" . 'Analyzing files...' . "\n", $output);
|
|
|
|
|
|
|
|
$this->assertSame(0, \Psalm\IssueBuffer::getErrorCount());
|
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
'Psalm was able to infer types for 100.000% of analyzed code (2 files)',
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary()
|
2018-10-17 17:03:32 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-02-24 19:48:57 +01:00
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function testAfterCodebasePopulatedIsInvoked()
|
|
|
|
{
|
|
|
|
$hook = new class implements AfterCodebasePopulatedInterface
|
|
|
|
{
|
|
|
|
/** @var bool */
|
|
|
|
public static $called = false;
|
|
|
|
/** @return void */
|
|
|
|
public static function afterCodebasePopulated(Codebase $codebase)
|
|
|
|
{
|
|
|
|
self::$called = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
|
|
|
Config::loadFromXML(
|
|
|
|
(string)getcwd(),
|
|
|
|
'<?xml version="1.0"?>
|
|
|
|
<psalm>
|
|
|
|
<projectFiles>
|
|
|
|
<directory name="tests/DummyProject" />
|
|
|
|
</projectFiles>
|
|
|
|
</psalm>'
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2019-03-01 14:45:18 +01:00
|
|
|
$hook_class = get_class($hook);
|
|
|
|
|
|
|
|
/** @psalm-suppress TypeCoercion see vimeo/psalm#1397 */
|
|
|
|
$this->project_analyzer->getCodebase()->config->after_codebase_populated[] = $hook_class;
|
2019-02-24 19:48:57 +01:00
|
|
|
|
|
|
|
ob_start();
|
|
|
|
$this->project_analyzer->check('tests/DummyProject');
|
2019-02-24 20:14:14 +01:00
|
|
|
ob_end_clean();
|
2019-02-24 19:48:57 +01:00
|
|
|
|
|
|
|
$this->assertTrue($hook::$called);
|
|
|
|
}
|
|
|
|
|
2018-10-17 17:03:32 +02:00
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function testCheckAfterNoChange()
|
|
|
|
{
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
2018-10-17 17:03:32 +02:00
|
|
|
Config::loadFromXML(
|
|
|
|
(string)getcwd(),
|
|
|
|
'<?xml version="1.0"?>
|
|
|
|
<psalm>
|
|
|
|
<projectFiles>
|
|
|
|
<directory name="tests/DummyProject" />
|
|
|
|
</projectFiles>
|
|
|
|
</psalm>'
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->output_format = \Psalm\Internal\Analyzer\ProjectAnalyzer::TYPE_JSON;
|
2018-10-17 17:03:32 +02:00
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->check('tests/DummyProject', true);
|
|
|
|
\Psalm\IssueBuffer::finish($this->project_analyzer, true, microtime(true));
|
2018-10-17 17:03:32 +02:00
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
'Psalm was able to infer types for 100.000% of analyzed code (2 files)',
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary()
|
2018-10-17 17:03:32 +02:00
|
|
|
);
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->getCodebase()->reloadFiles($this->project_analyzer, []);
|
2018-10-17 17:03:32 +02:00
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->check('tests/DummyProject', true);
|
2018-10-17 17:03:32 +02:00
|
|
|
|
|
|
|
$this->assertSame(0, \Psalm\IssueBuffer::getErrorCount());
|
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
'No files analyzed',
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary()
|
2018-10-17 17:03:32 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function testCheckAfterFileChange()
|
|
|
|
{
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
2018-10-17 17:03:32 +02:00
|
|
|
Config::loadFromXML(
|
|
|
|
(string)getcwd(),
|
|
|
|
'<?xml version="1.0"?>
|
|
|
|
<psalm>
|
|
|
|
<projectFiles>
|
|
|
|
<directory name="tests/DummyProject" />
|
|
|
|
</projectFiles>
|
|
|
|
</psalm>'
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->output_format = \Psalm\Internal\Analyzer\ProjectAnalyzer::TYPE_JSON;
|
2018-10-17 17:03:32 +02:00
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->check('tests/DummyProject', true);
|
|
|
|
\Psalm\IssueBuffer::finish($this->project_analyzer, true, microtime(true));
|
2018-10-17 17:03:32 +02:00
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
'Psalm was able to infer types for 100.000% of analyzed code (2 files)',
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary()
|
2018-10-17 17:03:32 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
$bat_file_path = getcwd() . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'DummyProject' . DIRECTORY_SEPARATOR . 'Bat.php';
|
|
|
|
|
|
|
|
$bat_replacement_contents = '<?php
|
|
|
|
|
2019-02-08 23:41:03 +01:00
|
|
|
namespace Vimeo\Test\DummyProject;
|
2018-10-17 17:03:32 +02:00
|
|
|
|
|
|
|
class Bat
|
|
|
|
{
|
|
|
|
public function __construct()
|
|
|
|
{
|
|
|
|
$a = new Bar();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
';
|
|
|
|
|
|
|
|
$this->file_provider->registerFile($bat_file_path, $bat_replacement_contents);
|
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->getCodebase()->reloadFiles($this->project_analyzer, []);
|
2018-10-17 17:03:32 +02:00
|
|
|
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->check('tests/DummyProject', true);
|
2018-10-17 17:03:32 +02:00
|
|
|
|
|
|
|
$this->assertSame(0, \Psalm\IssueBuffer::getErrorCount());
|
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
'Psalm was able to infer types for 100.000% of analyzed code (1 file)',
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary()
|
2018-10-17 17:03:32 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function testCheckDir()
|
|
|
|
{
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
2018-10-17 17:03:32 +02:00
|
|
|
Config::loadFromXML(
|
|
|
|
(string)getcwd(),
|
|
|
|
'<?xml version="1.0"?>
|
|
|
|
<psalm>
|
|
|
|
<projectFiles>
|
|
|
|
<directory name="tests/DummyProject" />
|
|
|
|
</projectFiles>
|
|
|
|
</psalm>'
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
ob_start();
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->checkDir('tests/DummyProject');
|
2018-10-17 17:03:32 +02:00
|
|
|
$output = ob_get_clean();
|
|
|
|
|
|
|
|
$this->assertSame('Scanning files...' . "\n" . 'Analyzing files...' . "\n", $output);
|
|
|
|
|
|
|
|
$this->assertSame(0, \Psalm\IssueBuffer::getErrorCount());
|
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
'Psalm was able to infer types for 100.000% of analyzed code (2 files)',
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary()
|
2018-10-17 17:03:32 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function testCheckPaths()
|
|
|
|
{
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
2018-10-17 17:03:32 +02:00
|
|
|
Config::loadFromXML(
|
|
|
|
(string)getcwd(),
|
|
|
|
'<?xml version="1.0"?>
|
|
|
|
<psalm>
|
|
|
|
<projectFiles>
|
|
|
|
<directory name="tests/DummyProject" />
|
|
|
|
</projectFiles>
|
|
|
|
</psalm>'
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
ob_start();
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->checkPaths(['tests/DummyProject/Bar.php']);
|
2018-10-17 17:03:32 +02:00
|
|
|
$output = ob_get_clean();
|
|
|
|
|
|
|
|
$this->assertSame('Scanning files...' . "\n" . 'Analyzing files...' . "\n", $output);
|
|
|
|
|
|
|
|
$this->assertSame(0, \Psalm\IssueBuffer::getErrorCount());
|
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
'Psalm was able to infer types for 100.000% of analyzed code (1 file)',
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary()
|
2018-10-17 17:03:32 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function testCheckFile()
|
|
|
|
{
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
|
2018-10-17 17:03:32 +02:00
|
|
|
Config::loadFromXML(
|
|
|
|
(string)getcwd(),
|
|
|
|
'<?xml version="1.0"?>
|
|
|
|
<psalm>
|
|
|
|
<projectFiles>
|
|
|
|
<directory name="tests/DummyProject" />
|
|
|
|
</projectFiles>
|
|
|
|
</psalm>'
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
ob_start();
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->checkFile('tests/DummyProject/Bar.php');
|
2018-10-17 17:03:32 +02:00
|
|
|
$output = ob_get_clean();
|
|
|
|
|
|
|
|
$this->assertSame('Scanning files...' . "\n" . 'Analyzing files...' . "\n", $output);
|
|
|
|
|
|
|
|
$this->assertSame(0, \Psalm\IssueBuffer::getErrorCount());
|
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
'Psalm was able to infer types for 100.000% of analyzed code (1 file)',
|
2018-11-11 18:01:14 +01:00
|
|
|
$this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary()
|
2018-10-17 17:03:32 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|