314 lines
12 KiB
PHP
314 lines
12 KiB
PHP
<?php
|
|
|
|
use phpDocumentor\Reflection\DocBlock\Tags\Link;
|
|
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url;
|
|
use phpDocumentor\Reflection\DocBlock\Tags\See;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
include __DIR__ . '/StubParser.php';
|
|
include __DIR__ . '/../vendor/autoload.php';
|
|
|
|
class ReflectionStubsSingleton
|
|
{
|
|
private static $reflectionStubs;
|
|
|
|
public static function getReflectionStubs(): stdClass
|
|
{
|
|
if (self::$reflectionStubs === null) {
|
|
$json = file_get_contents(__DIR__ . '/stub.json');
|
|
self::$reflectionStubs = json_decode($json);
|
|
}
|
|
return self::$reflectionStubs;
|
|
}
|
|
}
|
|
|
|
class PhpStormStubsSingleton
|
|
{
|
|
private static $phpstormStubs;
|
|
|
|
public static function getPhpStormStubs(): stdClass
|
|
{
|
|
if (self::$phpstormStubs === null) {
|
|
self::$phpstormStubs = getPhpStormStubs();
|
|
}
|
|
return self::$phpstormStubs;
|
|
}
|
|
}
|
|
|
|
class MutedProblems
|
|
{
|
|
/** @var stdClass */
|
|
private $mutedProblems;
|
|
|
|
public function __construct()
|
|
{
|
|
$json = file_get_contents(__DIR__ . '/mutedProblems.json');
|
|
$this->mutedProblems = json_decode($json);
|
|
}
|
|
|
|
public function getMutedProblemsForConstant(string $constantName): array
|
|
{
|
|
foreach ($this->mutedProblems->constants as $constant) {
|
|
if ($constant->name === $constantName) {
|
|
return $constant->problems;
|
|
}
|
|
}
|
|
return [];
|
|
}
|
|
|
|
public function getMutedProblemsForFunction(string $functionName): array
|
|
{
|
|
foreach ($this->mutedProblems->functions as $function) {
|
|
if ($function->name === $functionName) {
|
|
return $function->problems;
|
|
}
|
|
}
|
|
return [];
|
|
}
|
|
|
|
public function getMutedProblemsForClass(string $className): array
|
|
{
|
|
foreach ($this->mutedProblems->classes as $class) {
|
|
if ($class->name === $className && !empty($class->problems)) {
|
|
return $class->problems;
|
|
}
|
|
}
|
|
return [];
|
|
}
|
|
|
|
public function getMutedProblemsForMethod(string $className, $methodName): array
|
|
{
|
|
foreach ($this->mutedProblems->classes as $class) {
|
|
if ($class->name === $className && !empty($class->methods)) {
|
|
foreach ($class->methods as $method) {
|
|
if ($method->name === $methodName) {
|
|
return $method->problems;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return [];
|
|
}
|
|
|
|
public function getMutedProblemsForClassConstants($className, $constantName)
|
|
{
|
|
foreach ($this->mutedProblems->classes as $class) {
|
|
if ($class->name === $className && !empty($class->constants)) {
|
|
foreach ($class->constants as $constant) {
|
|
if ($constant->name === $constantName) {
|
|
return $constant->problems;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return [];
|
|
}
|
|
}
|
|
|
|
class TestStubs extends TestCase
|
|
{
|
|
/** @var MutedProblems */
|
|
private static $mutedProblems;
|
|
|
|
public static function setUpBeforeClass()/* The :void return type declaration that should be here would cause a BC issue */
|
|
{
|
|
self::$mutedProblems = new MutedProblems();
|
|
}
|
|
|
|
|
|
public function constantProvider()
|
|
{
|
|
foreach (ReflectionStubsSingleton::getReflectionStubs()->constants as $constant) {
|
|
yield "constant {$constant->name}" => [$constant];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider constantProvider
|
|
*/
|
|
public function testConstants(stdClass $constant)
|
|
{
|
|
$constantName = $constant->name;
|
|
$constantValue = $constant->value;
|
|
$stubConstants = PhpStormStubsSingleton::getPhpStormStubs()->constants;
|
|
if (in_array('missing constant', self::$mutedProblems->getMutedProblemsForConstant($constantName), true)) {
|
|
$this->markTestSkipped('constant is excluded');
|
|
}
|
|
$this->assertArrayHasKey($constantName, $stubConstants, "Missing constant: const $constantName = $constantValue\n");
|
|
}
|
|
|
|
|
|
public function functionProvider()
|
|
{
|
|
foreach (ReflectionStubsSingleton::getReflectionStubs()->functions as $function) {
|
|
yield "function {$function->name}" => [$function];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider functionProvider
|
|
*/
|
|
public function testFunctions(stdClass $function)
|
|
{
|
|
$functionName = $function->name;
|
|
$stubFunctions = PhpStormStubsSingleton::getPhpStormStubs()->functions;
|
|
$params = $this->getParameterRepresentation($function);
|
|
if (in_array('missing function', self::$mutedProblems->getMutedProblemsForFunction($functionName), true)) {
|
|
$this->markTestSkipped('function is excluded');
|
|
}
|
|
$this->assertArrayHasKey($functionName, $stubFunctions, "Missing function: function $functionName($params){}");
|
|
$phpstormFunction = $stubFunctions[$functionName];
|
|
if (!in_array('deprecated function', self::$mutedProblems->getMutedProblemsForFunction($functionName), true)) {
|
|
$this->assertFalse($function->is_deprecated && $phpstormFunction->is_deprecated !== true, "Function $functionName is not deprecated in stubs");
|
|
}
|
|
if (!in_array('parameter mismatch', self::$mutedProblems->getMutedProblemsForFunction($functionName), true)) {
|
|
$this->assertSameSize($function->parameters, $phpstormFunction->parameters,
|
|
"Parameter number mismatch for function $functionName. Expected: " . $this->getParameterRepresentation($function));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
public function classProvider()
|
|
{
|
|
foreach (ReflectionStubsSingleton::getReflectionStubs()->classes as $class) {
|
|
//exclude classes from PHPReflectionParser
|
|
if (0 !== strncmp($class->name, 'PHP', 3)) {
|
|
yield "class {$class->name}" => [$class];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider classProvider
|
|
*/
|
|
public function testClasses(stdClass $class)
|
|
{
|
|
$className = $class->name;
|
|
$stubClasses = PhpStormStubsSingleton::getPhpStormStubs()->classes;
|
|
if (in_array('missing class', self::$mutedProblems->getMutedProblemsForClass($className), true)) {
|
|
$this->markTestSkipped('class is skipped');
|
|
}
|
|
$this->assertArrayHasKey($className, $stubClasses, "Missing class $className: class $className {}");
|
|
$stubClass = $stubClasses[$className];
|
|
if (!in_array('wrong parent', self::$mutedProblems->getMutedProblemsForClass($className), true)) {
|
|
$this->assertEquals($class->parentClass, $stubClass->parentClass, "Class $className should extend {$class->parentClass}");
|
|
}
|
|
foreach ($class->constants as $constant) {
|
|
if (!in_array('missing constant', self::$mutedProblems->getMutedProblemsForClassConstants($className, $constant->name), true)) {
|
|
$this->assertArrayHasKey($constant->name, $stubClass->constants, "Missing constant $className::{$constant->name}");
|
|
}
|
|
}
|
|
// @todo check interfaces
|
|
// @todo check traits
|
|
foreach ($class->methods as $method) {
|
|
$params = $this->getParameterRepresentation($method);
|
|
$methodName = $method->name;
|
|
if (!in_array('missing method', self::$mutedProblems->getMutedProblemsForMethod($className, $methodName), true)) {
|
|
$this->assertArrayHasKey($methodName, $stubClass->methods, "Missing method $className::$methodName($params){}");
|
|
$stubMethod = $stubClass->methods[$methodName];
|
|
if (!in_array('not final', self::$mutedProblems->getMutedProblemsForMethod($className, $methodName), true)) {
|
|
$this->assertEquals($method->is_final, $stubMethod->is_final, "Method $className::$methodName final modifier is incorrect");
|
|
}
|
|
if (!in_array('not static', self::$mutedProblems->getMutedProblemsForMethod($className, $methodName), true)) {
|
|
$this->assertEquals($method->is_static, $stubMethod->is_static, "Method $className::$methodName static modifier is incorrect");
|
|
}
|
|
if (!in_array('access modifiers', self::$mutedProblems->getMutedProblemsForMethod($className, $methodName), true)) {
|
|
$this->assertEquals($method->access, $stubMethod->access, "Method $className::$methodName access modifier is incorrect");
|
|
}
|
|
if (!in_array('parameter mismatch', self::$mutedProblems->getMutedProblemsForMethod($className, $methodName), true)) {
|
|
$this->assertSameSize($method->parameters, $stubMethod->parameters, "Parameter number mismatch for method $className::$methodName. Expected: " . $this->getParameterRepresentation($method));
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public function stubFunctionProvider()
|
|
{
|
|
foreach (PhpStormStubsSingleton::getPhpStormStubs()->functions as $functionName => $function) {
|
|
yield "function {$functionName}" => [$function];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider stubFunctionProvider
|
|
*/
|
|
public function testFunctionPHPDocs(stdClass $function)
|
|
{
|
|
$this->assertNull($function->parseError, $function->parseError ?: "");
|
|
$this->checkLinks($function, "function $function->name");
|
|
}
|
|
|
|
public function stubClassProvider()
|
|
{
|
|
foreach (PhpStormStubsSingleton::getPhpStormStubs()->classes as $className => $class) {
|
|
yield "class {$className}" => [$class];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider stubClassProvider
|
|
*/
|
|
public function testClassesPHPDocs(stdClass $class)
|
|
{
|
|
$this->assertNull($class->parseError, $class->parseError ?: "");
|
|
$this->checkLinks($class, "class $class->name");
|
|
}
|
|
|
|
public function stubMethodProvider()
|
|
{
|
|
foreach (PhpStormStubsSingleton::getPhpStormStubs()->classes as $className => $class) {
|
|
foreach ($class->methods as $methodName => $method) {
|
|
yield "Method {$className}::{$methodName}" => [$methodName, $method];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider stubMethodProvider
|
|
*/
|
|
public function testMethodsPHPDocs(string $methodName, stdClass $method)
|
|
{
|
|
if ($methodName === "__construct") {
|
|
$this->assertNull($method->returnTag, "@return tag for __construct should be omitted");
|
|
}
|
|
$this->assertNull($method->parseError, $method->parseError ?: "");
|
|
$this->checkLinks($method, "method $methodName");
|
|
}
|
|
|
|
private function getParameterRepresentation(stdClass $function): string
|
|
{
|
|
$result = '';
|
|
foreach ($function->parameters as $parameter) {
|
|
if (!empty($parameter->type)) {
|
|
$result .= $parameter->type . ' ';
|
|
}
|
|
if ($parameter->is_passed_by_ref) {
|
|
$result .= '&';
|
|
}
|
|
if ($parameter->is_vararg) {
|
|
$result .= '...';
|
|
}
|
|
$result .= '$' . $parameter->name . ', ';
|
|
}
|
|
$result = rtrim($result, ', ');
|
|
return $result;
|
|
}
|
|
|
|
private function checkLinks($element, $elementName): void
|
|
{
|
|
foreach ($element->links as $link) {
|
|
if ($link instanceof Link) {
|
|
$this->assertStringStartsWith('https', $link->getLink(), "In $elementName @link doesn't start with https");
|
|
}
|
|
}
|
|
foreach ($element->see as $see) {
|
|
if ($see instanceof See && $see->getReference() instanceof Url) {
|
|
if (strpos($see, 'http') === 0) {
|
|
$this->assertStringStartsWith('https', $see, "In $elementName @see doesn't start with https");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |