1
0
Fork 0
php-language-server/tests/Validation/ValidationTest.php

205 lines
7.9 KiB
PHP
Raw Normal View History

2017-03-15 21:10:29 +00:00
<?php
declare(strict_types = 1);
namespace LanguageServer\Tests;
use Exception;
2017-05-01 23:50:42 +00:00
use LanguageServer\Definition;
2017-03-15 21:10:29 +00:00
use LanguageServer\Index\Index;
use LanguageServer\ParserKind;
2017-03-15 21:10:29 +00:00
use LanguageServer\ParserResourceFactory;
use LanguageServer\PhpDocument;
2017-05-01 23:50:42 +00:00
use phpDocumentor\Reflection\DocBlock;
2017-03-15 21:10:29 +00:00
use phpDocumentor\Reflection\DocBlockFactory;
use PHPUnit\Framework\TestCase;
use LanguageServer\ClientHandler;
use LanguageServer\Protocol\Message;
use AdvancedJsonRpc;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use Sabre\Event\Loop;
use Microsoft\PhpParser as Tolerant;
2017-03-15 21:10:29 +00:00
class ValidationTest extends TestCase
{
public function frameworkErrorProvider() {
$frameworks = glob(__DIR__ . "/../../validation/frameworks/*", GLOB_ONLYDIR);
$testProviderArray = array();
foreach ($frameworks as $frameworkDir) {
$frameworkName = basename($frameworkDir);
if ($frameworkName !== '_cases') {
continue;
}
2017-05-01 23:50:42 +00:00
2017-03-15 21:10:29 +00:00
$iterator = new RecursiveDirectoryIterator(__DIR__ . "/../../validation/frameworks/" . $frameworkName);
$skipped = json_decode(file_get_contents(__DIR__ . '/skipped.json'));
2017-03-15 21:10:29 +00:00
foreach (new RecursiveIteratorIterator($iterator) as $file) {
if (strpos(\strrev((string)$file), \strrev(".php")) === 0 && !\in_array(basename((string)$file), $skipped)) {
if ($file->getSize() < 100000) {
2017-03-24 17:30:07 +00:00
$testProviderArray[$frameworkName . "::" . $file->getBasename()] = [$file->getPathname(), $frameworkName];
}
2017-03-15 21:10:29 +00:00
}
}
}
if (count($testProviderArray) === 0) {
throw new Exception("ERROR: Validation testsuite frameworks not found - run `git submodule update --init --recursive` to download.");
}
return $testProviderArray;
}
/**
* @group validation
* @dataProvider frameworkErrorProvider
2017-05-01 23:50:42 +00:00
* @param $testCaseFile
* @param $frameworkName
*/
public function testDefinitionErrors($testCaseFile, $frameworkName) {
echo "Test file: " . realpath($testCaseFile) . PHP_EOL;
2017-05-01 23:50:42 +00:00
$fileContents = file_get_contents($testCaseFile);
$expectedValues = $this->getExpectedTestValues($testCaseFile, $frameworkName, $fileContents);
$actualValues = $this->getActualTestValues($testCaseFile, $frameworkName, $fileContents);
2017-05-01 23:50:42 +00:00
try {
$this->assertEquals($expectedValues['definitions'], $actualValues['definitions']);
try {
$this->assertArraySubset((array)$expectedValues['references'], (array)$actualValues['references'], false, 'references don\'t match.');
} catch (\Throwable $e) {
$this->assertEquals((array)$expectedValues['references'], (array)$actualValues['references'], 'references don\'t match.');
}
} catch (\Throwable $e) {
$outputFile = getExpectedValuesFile($testCaseFile);
if ($frameworkName === '_cases') {
file_put_contents($outputFile, json_encode($actualValues, JSON_PRETTY_PRINT));
}
throw $e;
2017-05-01 23:50:42 +00:00
}
}
2017-05-01 23:50:42 +00:00
/**
* @param $filename
* @param $frameworkName
2017-05-01 23:50:42 +00:00
* @param $fileContents
* @return array
2017-05-01 23:50:42 +00:00
*/
private function getExpectedTestValues($filename, $frameworkName, $fileContents) {
2017-05-01 23:50:42 +00:00
global $parserKind;
$parserKind = ParserKind::PHP_PARSER;
$outputFile = getExpectedValuesFile($filename);
if (file_exists($outputFile)) {
return (array)json_decode(file_get_contents($outputFile));
}
2017-05-01 23:50:42 +00:00
$index = new Index();
$parser = ParserResourceFactory::getParser();
$docBlockFactory = DocBlockFactory::createInstance();
$definitionResolver = ParserResourceFactory::getDefinitionResolver($index);
2017-05-01 23:50:42 +00:00
try {
$document = new PhpDocument($filename, $fileContents, $index, $parser, $docBlockFactory, $definitionResolver);
} catch (\Throwable $e) {
$this->markTestSkipped('Baseline parser failed: '. $e->getTraceAsString());
}
2017-04-12 19:51:15 +00:00
2017-05-01 23:50:42 +00:00
if ($document->getStmts() === null) {
$this->markTestSkipped('Baseline parser failed: null AST');
}
2017-04-12 19:51:15 +00:00
$expectedRefs = $index->references;
$this->filterSkippedReferences($expectedRefs);
$expectedDefs = $this->getTestValuesFromDefs($document->getDefinitions());
$refsAndDefs = array(
'references' => json_decode(json_encode($expectedRefs)),
'definitions' => json_decode(json_encode($expectedDefs))
);
return $refsAndDefs;
2017-05-01 23:50:42 +00:00
}
private function getActualTestValues($filename, $frameworkName, $fileContents): array {
2017-05-01 23:50:42 +00:00
global $parserKind;
$parserKind = ParserKind::TOLERANT_PHP_PARSER;
$index = new Index();
$parser = ParserResourceFactory::getParser();
$docBlockFactory = DocBlockFactory::createInstance();
$definitionResolver = ParserResourceFactory::getDefinitionResolver($index);
$document = new PhpDocument($filename, $fileContents, $index, $parser, $docBlockFactory, $definitionResolver);
$actualRefs = $index->references;
$this->filterSkippedReferences($actualRefs);
$actualDefs = $this->getTestValuesFromDefs($document->getDefinitions());
// TODO - there's probably a more PHP-typical way to do this. Need to compare the objects parsed from json files
// to the real results. json_decode returns stdClass Objects, not arrays.
$refsAndDefs = array(
'references' => json_decode(json_encode($actualRefs)),
'definitions' => json_decode(json_encode($actualDefs))
);
return $refsAndDefs;
2017-05-01 23:50:42 +00:00
}
/**
* @param $definitions Definition[]
2017-05-01 23:50:42 +00:00
* @return array|\array[]
*/
private function getTestValuesFromDefs($definitions): array
2017-05-01 23:50:42 +00:00
{
// TODO - use reflection to read these properties
$propertyNames = ['extends', 'isGlobal', 'isStatic', 'canBeInstantiated', 'symbolInformation', 'type', 'documentation'];
$defsForAssert = [];
foreach ($definitions as $definition) {
$fqn = $definition->fqn;
2017-05-01 23:50:42 +00:00
foreach ($propertyNames as $propertyName) {
if ($propertyName === 'symbolInformation') {
// Range is very often different - don't check it, for now
unset($definition->$propertyName->location->range);
2017-05-01 23:50:42 +00:00
} elseif ($propertyName === 'extends') {
$definition->$propertyName = $definition->$propertyName ?? [];
} elseif ($propertyName === 'type') {
// Class info is not captured by json_encode. It's important for 'type'.
$defsForAssert[$fqn][$propertyName . '__class'] = get_class($definition->$propertyName);
}
$defsForAssert[$fqn][$propertyName] = $definition->$propertyName;
}
2017-05-01 23:50:42 +00:00
}
return $defsForAssert;
2017-05-01 23:50:42 +00:00
}
private function filterSkippedReferences(&$references): void
2017-05-01 23:50:42 +00:00
{
$skipped = [
'false', 'true', 'null', 'FALSE', 'TRUE', 'NULL',
'__', // magic constants are treated as normal constants
'Exception', 'Error', // catch exception types missing from old definition resolver
'Trait', // use Trait references are missing from old definition resolve
'->tableAlias', '->realField', '->field', '->first_name', '->last_name', '->quoteMatch', '->idCol', '->timeCol', '->dataCol',
'pathToUri', 'uriToPath' // group function use declarations are broken in old definition resolver
];
foreach ($references as $key=>$value) {
foreach ($skipped as $s) {
if (strpos($key, $s) !== false) {
unset($references[$key]);
2017-05-01 23:50:42 +00:00
}
}
}
}
}
function getExpectedValuesFile($testCaseFile): string {
return $testCaseFile . '.expected.json';
2017-03-15 21:10:29 +00:00
}