file_provider = new FakeFileProvider(); } private function getProjectAnalyzerWithConfig(Config $config): ProjectAnalyzer { $config->setIncludeCollector(new IncludeCollector()); return new ProjectAnalyzer( $config, new Providers( $this->file_provider, new ParserInstanceCacheProvider(), new FileStorageInstanceCacheProvider(), new ClassLikeStorageInstanceCacheProvider(), new FakeFileReferenceCacheProvider(), new ProjectCacheProvider(), ), new ReportOptions(), ); } public function testCheck(): void { $this->project_analyzer = $this->getProjectAnalyzerWithConfig( Config::loadFromXML( (string)getcwd(), ' ', ), ); $this->project_analyzer->setPhpVersion('8.1', 'tests'); $this->project_analyzer->progress = new EchoProgress(); ob_start(); $this->project_analyzer->check('tests/fixtures/DummyProject'); $output = ob_get_clean(); $this->assertStringContainsString('Target PHP version: 8.1 (set by tests)', $output); $this->assertStringContainsString('Scanning files...', $output); $this->assertStringContainsString('Analyzing files...', $output); $this->assertSame(0, IssueBuffer::getErrorCount()); $codebase = $this->project_analyzer->getCodebase(); $this->assertSame([0, 5], $codebase->analyzer->getTotalTypeCoverage($codebase)); $this->assertSame( 'Psalm was able to infer types for 100% of the codebase', $codebase->analyzer->getTypeInferenceSummary( $codebase, ), ); } public function testAfterCodebasePopulatedIsInvoked(): void { $hook = new class implements AfterCodebasePopulatedInterface { public static bool $called = false; /** * @return void * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint */ public static function afterCodebasePopulated(AfterCodebasePopulatedEvent $event) { self::$called = true; } }; $this->project_analyzer = $this->getProjectAnalyzerWithConfig( Config::loadFromXML( (string)getcwd(), ' ', ), ); $hook_class = get_class($hook); $this->project_analyzer->getCodebase()->config->eventDispatcher->after_codebase_populated[] = $hook_class; ob_start(); $this->project_analyzer->check('tests/fixtures/DummyProject'); ob_end_clean(); $this->assertTrue($hook::$called); } public function testCheckAfterNoChange(): void { $this->project_analyzer = $this->getProjectAnalyzerWithConfig( Config::loadFromXML( (string)getcwd(), ' ', ), ); $this->assertNotNull($this->project_analyzer->stdout_report_options); $this->project_analyzer->stdout_report_options->format = Report::TYPE_JSON; $this->project_analyzer->check('tests/fixtures/DummyProject', true); ob_start(); IssueBuffer::finish($this->project_analyzer, true, microtime(true)); ob_end_clean(); $this->assertSame( 'Psalm was able to infer types for 100% of the codebase', $this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary( $this->project_analyzer->getCodebase(), ), ); $this->project_analyzer->getCodebase()->reloadFiles($this->project_analyzer, []); $this->project_analyzer->check('tests/fixtures/DummyProject', true); $this->assertSame(0, IssueBuffer::getErrorCount()); $this->assertSame( 'Psalm was able to infer types for 100% of the codebase', $this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary( $this->project_analyzer->getCodebase(), ), ); } public function testCheckAfterFileChange(): void { $this->project_analyzer = $this->getProjectAnalyzerWithConfig( Config::loadFromXML( (string)getcwd(), ' ', ), ); $this->assertNotNull($this->project_analyzer->stdout_report_options); $this->project_analyzer->stdout_report_options->format = Report::TYPE_JSON; $this->project_analyzer->check('tests/fixtures/DummyProject', true); ob_start(); IssueBuffer::finish($this->project_analyzer, true, microtime(true)); ob_end_clean(); $this->assertSame( 'Psalm was able to infer types for 100% of the codebase', $this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary( $this->project_analyzer->getCodebase(), ), ); $bat_file_path = getcwd() . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'DummyProject' . DIRECTORY_SEPARATOR . 'Bat.php'; $bat_replacement_contents = 'file_provider->registerFile($bat_file_path, $bat_replacement_contents); $this->project_analyzer->getCodebase()->reloadFiles($this->project_analyzer, []); $this->project_analyzer->check('tests/fixtures/DummyProject', true); $this->assertSame(0, IssueBuffer::getErrorCount()); $this->assertSame( 'Psalm was able to infer types for 100% of the codebase', $this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary( $this->project_analyzer->getCodebase(), ), ); } public function testCheckDir(): void { $this->project_analyzer = $this->getProjectAnalyzerWithConfig( Config::loadFromXML( (string)getcwd(), ' ', ), ); $this->project_analyzer->setPhpVersion('8.1', 'tests'); $this->project_analyzer->progress = new EchoProgress(); ob_start(); $this->project_analyzer->checkDir('tests/fixtures/DummyProject'); $output = ob_get_clean(); $this->assertStringContainsString('Target PHP version: 8.1 (set by tests)', $output); $this->assertStringContainsString('Scanning files...', $output); $this->assertStringContainsString('Analyzing files...', $output); $this->assertSame(0, IssueBuffer::getErrorCount()); $this->assertSame( 'Psalm was able to infer types for 100% of the codebase', $this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary( $this->project_analyzer->getCodebase(), ), ); } public function testCheckPaths(): void { $this->project_analyzer = $this->getProjectAnalyzerWithConfig( Config::loadFromXML( (string)getcwd(), ' ', ), ); $this->project_analyzer->setPhpVersion('8.1', 'tests'); $this->project_analyzer->progress = new EchoProgress(); ob_start(); // checkPaths expects absolute paths, // otherwise it's unable to match them against configured folders $this->project_analyzer->checkPaths([ realpath(getcwd() . '/tests/fixtures/DummyProject/Bar.php'), realpath(getcwd() . '/tests/fixtures/DummyProject/SomeTrait.php'), ]); $output = ob_get_clean(); $this->assertStringContainsString('Target PHP version: 8.1 (set by tests)', $output); $this->assertStringContainsString('Scanning files...', $output); $this->assertStringContainsString('Analyzing files...', $output); $this->assertSame(0, IssueBuffer::getErrorCount()); $this->assertSame( 'Psalm was able to infer types for 100% of the codebase', $this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary( $this->project_analyzer->getCodebase(), ), ); } public function testCheckFile(): void { $this->project_analyzer = $this->getProjectAnalyzerWithConfig( Config::loadFromXML( (string)getcwd(), ' ', ), ); $this->project_analyzer->setPhpVersion('8.1', 'tests'); $this->project_analyzer->progress = new EchoProgress(); ob_start(); // checkPaths expects absolute paths, // otherwise it's unable to match them against configured folders $this->project_analyzer->checkPaths([ realpath(getcwd() . '/tests/fixtures/DummyProject/Bar.php'), realpath(getcwd() . '/tests/fixtures/DummyProject/SomeTrait.php'), ]); $output = ob_get_clean(); $this->assertStringContainsString('Target PHP version: 8.1 (set by tests)', $output); $this->assertStringContainsString('Scanning files...', $output); $this->assertStringContainsString('Analyzing files...', $output); $this->assertSame(0, IssueBuffer::getErrorCount()); $this->assertSame( 'Psalm was able to infer types for 100% of the codebase', $this->project_analyzer->getCodebase()->analyzer->getTypeInferenceSummary( $this->project_analyzer->getCodebase(), ), ); } }