Skip to content

Commit 882c473

Browse files
authored
Merge pull request #10 from mglaman/phpunit
Add PHPUnit and automate testing
2 parents 42c2de2 + 509ac53 commit 882c473

9 files changed

+247
-29
lines changed

.editorconfig

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1+
# This file is for unifying the coding style for different editors and IDEs
2+
# editorconfig.org
3+
4+
# PHP PSR-2 Coding Standards
5+
# http://www.php-fig.org/psr/psr-2/
6+
7+
root = true
8+
19
[*.php]
10+
charset = utf-8
211
end_of_line = lf
312
insert_final_newline = true
413
trim_trailing_whitespace = true
5-
charset = utf-8
614
indent_style = space
15+
indent_size = 4
16+
717
[*.neon]
818
indent_style = tab

.gitignore

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
composer.phar
22
composer.lock
33
/vendor/
4-
5-
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
6-
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
7-
# composer.lock
4+
/clover.xml

.travis.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ php:
44
- 7.2
55
- 7.3
66

7-
before_script:
8-
- if php --ri xdebug >/dev/null; then phpenv config-rm xdebug.ini; fi
97
before_install:
108
- composer global require "hirak/prestissimo:^0.3"
119
install:
@@ -14,6 +12,7 @@ script:
1412
# Inspections
1513
- ./vendor/bin/phpcs src
1614
- ./vendor/bin/phpstan analyze src
15+
- ./vendor/bin/phpunit
1716

1817
# Try to add to a Drupal setup.
1918
- composer create-project drupal-composer/drupal-project:8.x-dev $TRAVIS_BUILD_DIR/../drupal --no-interaction

composer.json

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,25 @@
1414
"symfony/yaml": "~3.4.5|^4.2",
1515
"webflo/drupal-finder": "^1.1"
1616
},
17-
"autoload": {
18-
"psr-4": {
19-
"PHPStan\\": "src/"
20-
}
21-
},
2217
"require-dev": {
2318
"phpstan/phpstan-strict-rules": "^0.10.1",
24-
"squizlabs/php_codesniffer": "^3.3"
19+
"squizlabs/php_codesniffer": "^3.3",
20+
"phpunit/phpunit": "^7.5"
2521
},
2622
"suggest": {
2723
"phpstan/phpstan-deprecation-rules": "For catching deprecations, especially in Drupal core."
2824
},
29-
"extra": {
30-
"branch-alias": {
31-
"dev-master": "0.11-dev"
32-
}
33-
}
25+
"autoload": {
26+
"psr-4": {
27+
"PHPStan\\": "src/"
28+
}
29+
},
30+
"autoload-dev": {
31+
"classmap": ["tests/"]
32+
},
33+
"extra": {
34+
"branch-alias": {
35+
"dev-master": "0.11-dev"
36+
}
37+
}
3438
}

phpunit.xml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd"
4+
bootstrap="vendor/autoload.php"
5+
forceCoversAnnotation="true"
6+
beStrictAboutCoversAnnotation="true"
7+
beStrictAboutOutputDuringTests="true"
8+
beStrictAboutTodoAnnotatedTests="true"
9+
verbose="true">
10+
<testsuites>
11+
<testsuite name="default">
12+
<directory suffix="Test.php">tests</directory>
13+
</testsuite>
14+
</testsuites>
15+
<logging>
16+
<log type="coverage-text" target="php://stdout" showUncoveredFiles="true" lowUpperBound="50" highLowerBound="80" />
17+
<log type="coverage-clover" target="./clover.xml" />
18+
</logging>
19+
<filter>
20+
<whitelist processUncoveredFilesFromWhitelist="true">
21+
<directory suffix=".php">src</directory>
22+
<exclude>
23+
<directory>./vendor</directory>
24+
<directory>./tests</directory>
25+
</exclude>
26+
</whitelist>
27+
</filter>
28+
</phpunit>

src/Drupal/ServiceMap.php

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,21 @@ public function __construct(array $drupalServices)
1818
foreach ($drupalServices as $serviceId => $serviceDefinition) {
1919
// @todo support factories
2020
if (!isset($serviceDefinition['class'])) {
21-
continue;
21+
if (isset($serviceDefinition['alias'], $drupalServices[$serviceDefinition['alias']])) {
22+
$serviceDefinition['class'] = $drupalServices[$serviceDefinition['alias']]['class'];
23+
} else {
24+
continue;
25+
}
2226
}
2327
$this->services[$serviceId] = new DrupalServiceDefinition(
24-
$serviceId,
28+
(string) $serviceId,
2529
$serviceDefinition['class'],
2630
$serviceDefinition['public'] ?? true,
2731
$serviceDefinition['alias'] ?? null
2832
);
2933
}
3034
}
3135

32-
/**
33-
* @return DrupalServiceDefinition[]
34-
*/
35-
public function getServices(): array
36-
{
37-
return $this->services;
38-
}
39-
4036
public function getService(string $id): ?DrupalServiceDefinition
4137
{
4238
return $this->services[$id] ?? null;

src/Type/EntityTypeManagerGetStorageDynamicReturnTypeExtension.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPStan\Analyser\Scope;
99
use PHPStan\Reflection\MethodReflection;
1010
use PHPStan\Reflection\ParametersAcceptorSelector;
11+
use PHPStan\ShouldNotHappenException;
1112

1213
class EntityTypeManagerGetStorageDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
1314
{
@@ -38,7 +39,8 @@ public function getTypeFromMethodCall(
3839
): Type {
3940
$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
4041
if (!isset($methodCall->args[0])) {
41-
return $returnType;
42+
// Parameter is required.
43+
throw new ShouldNotHappenException();
4244
}
4345

4446
$arg1 = $methodCall->args[0]->value;
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
namespace PHPStan\Drupal;
4+
5+
use PhpParser\Node\Arg;
6+
use PhpParser\Node\Expr;
7+
use PhpParser\Node\Expr\MethodCall;
8+
use PhpParser\Node\Scalar\String_;
9+
use PHPStan\Analyser\Scope;
10+
use PHPStan\Reflection\MethodReflection;
11+
use PHPStan\Reflection\ParametersAcceptor;
12+
use PHPStan\ShouldNotHappenException;
13+
use PHPStan\Type\EntityTypeManagerGetStorageDynamicReturnTypeExtension;
14+
use PHPStan\Type\ObjectType;
15+
use PHPUnit\Framework\TestCase;
16+
17+
final class EntityTypeManagerGetStorageDynamicReturnTypeExtensionTest extends TestCase
18+
{
19+
20+
/**
21+
* @covers \PHPStan\Type\EntityTypeManagerGetStorageDynamicReturnTypeExtension::__construct
22+
* @covers \PHPStan\Type\EntityTypeManagerGetStorageDynamicReturnTypeExtension::getClass
23+
*/
24+
public function testGetClass()
25+
{
26+
$x = new EntityTypeManagerGetStorageDynamicReturnTypeExtension([]);
27+
self::assertEquals('Drupal\Core\Entity\EntityTypeManagerInterface', $x->getClass());
28+
}
29+
30+
/**
31+
* @dataProvider getEntityStorageProvider
32+
*
33+
* @covers \PHPStan\Type\EntityTypeManagerGetStorageDynamicReturnTypeExtension::__construct
34+
* @covers \PHPStan\Type\EntityTypeManagerGetStorageDynamicReturnTypeExtension::getTypeFromMethodCall
35+
*/
36+
public function testGetTypeFromMethodCall($entityType, $storageClass)
37+
{
38+
$x = new EntityTypeManagerGetStorageDynamicReturnTypeExtension([
39+
'node' => 'Drupal\node\NodeStorage',
40+
'search_api_index' => 'Drupal\search_api\Entity\SearchApiConfigEntityStorage',
41+
]);
42+
43+
$methodReflection = $this->prophesize(MethodReflection::class);
44+
$methodReflection->getName()->willReturn('getStorage');
45+
46+
$defaultObjectType = $this->prophesize(ObjectType::class);
47+
$defaultObjectType->getClassName()->willReturn('Drupal\Core\Entity\EntityStorageInterface');
48+
$variantsParametersAcceptor = $this->prophesize(ParametersAcceptor::class);
49+
$variantsParametersAcceptor->getReturnType()->willReturn($defaultObjectType->reveal());
50+
$methodReflection->getVariants()->willReturn([$variantsParametersAcceptor->reveal()]);
51+
52+
if ($entityType === null) {
53+
$this->expectException(ShouldNotHappenException::class);
54+
$methodCall = new MethodCall(
55+
$this->prophesize(Expr::class)->reveal(),
56+
'getStorage'
57+
);
58+
} else {
59+
$methodCall = new MethodCall(
60+
$this->prophesize(Expr::class)->reveal(),
61+
'getStorage',
62+
[new Arg($entityType)]
63+
);
64+
}
65+
66+
$scope = $this->prophesize(Scope::class);
67+
68+
$type = $x->getTypeFromMethodCall(
69+
$methodReflection->reveal(),
70+
$methodCall,
71+
$scope->reveal()
72+
);
73+
self::assertInstanceOf(ObjectType::class, $type);
74+
assert($type instanceof ObjectType);
75+
self::assertEquals($storageClass, $type->getClassName());
76+
}
77+
78+
public function getEntityStorageProvider(): \Iterator
79+
{
80+
yield [new String_('node'), 'Drupal\node\NodeStorage'];
81+
yield [new String_('user'), 'Drupal\Core\Entity\EntityStorageInterface'];
82+
yield [new String_('search_api_index'), 'Drupal\search_api\Entity\SearchApiConfigEntityStorage'];
83+
yield [null, null];
84+
yield [$this->prophesize(MethodCall::class)->reveal(), 'Drupal\Core\Entity\EntityStorageInterface'];
85+
yield [$this->prophesize(Expr\StaticCall::class)->reveal(), 'Drupal\Core\Entity\EntityStorageInterface'];
86+
yield [$this->prophesize(Expr\BinaryOp\Concat::class)->reveal(), 'Drupal\Core\Entity\EntityStorageInterface'];
87+
}
88+
89+
/**
90+
* @covers \PHPStan\Type\EntityTypeManagerGetStorageDynamicReturnTypeExtension::__construct
91+
* @covers \PHPStan\Type\EntityTypeManagerGetStorageDynamicReturnTypeExtension::isMethodSupported
92+
*/
93+
public function testIsMethodSupported()
94+
{
95+
$x = new EntityTypeManagerGetStorageDynamicReturnTypeExtension([]);
96+
97+
$valid = $this->prophesize(MethodReflection::class);
98+
$valid->getName()->willReturn('getStorage');
99+
self::assertTrue($x->isMethodSupported($valid->reveal()));
100+
101+
$invalid = $this->prophesize(MethodReflection::class);
102+
$invalid->getName()->willReturn('getAccessControlHandler');
103+
self::assertFalse($x->isMethodSupported($invalid->reveal()));
104+
}
105+
}

tests/ServiceMapFactoryTest.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
namespace PHPStan\Drupal;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
final class ServiceMapFactoryTest extends TestCase
8+
{
9+
10+
/**
11+
* @dataProvider getServiceProvider
12+
*
13+
* @covers \PHPStan\Drupal\ServiceMapFactory::__construct
14+
* @covers \PHPStan\Drupal\ServiceMapFactory::create
15+
* @covers \PHPStan\Drupal\DrupalServiceDefinition::__construct
16+
* @covers \PHPStan\Drupal\DrupalServiceDefinition::getClass
17+
* @covers \PHPStan\Drupal\DrupalServiceDefinition::isPublic
18+
* @covers \PHPStan\Drupal\DrupalServiceDefinition::getAlias
19+
* @covers \PHPStan\Drupal\DrupalServiceDefinition::getId
20+
* @covers \PHPStan\Drupal\ServiceMap::__construct
21+
* @covers \PHPStan\Drupal\ServiceMap::getService
22+
*/
23+
public function testFactory(string $id, callable $validator)
24+
{
25+
$factory = new ServiceMapFactory([
26+
'entity_type.manager' => [
27+
'class' => 'Drupal\Core\Entity\EntityTypeManager'
28+
],
29+
'skipped_factory' => [
30+
'factory' => 'cache_factory:get',
31+
'arguments' => ['cache'],
32+
],
33+
'config.storage.staging' => [
34+
'class' => 'Drupal\Core\Config\FileStorage',
35+
],
36+
'config.storage.sync' => [
37+
'alias' => 'config.storage.staging',
38+
]
39+
]);
40+
$validator($factory->create()->getService($id));
41+
}
42+
43+
public function getServiceProvider(): \Iterator
44+
{
45+
yield [
46+
'unknown',
47+
function (?DrupalServiceDefinition $service): void {
48+
self::assertNull($service, 'unknown');
49+
},
50+
];
51+
yield [
52+
'entity_type.manager',
53+
function (?DrupalServiceDefinition $service): void {
54+
self::assertNotNull($service);
55+
self::assertEquals('entity_type.manager', $service->getId());
56+
self::assertEquals('Drupal\Core\Entity\EntityTypeManager', $service->getClass());
57+
self::assertTrue($service->isPublic());
58+
self::assertNull($service->getAlias());
59+
}
60+
];
61+
// For now factories are skipped.
62+
yield [
63+
'skipped_factory',
64+
function (?DrupalServiceDefinition $service): void {
65+
self::assertNull($service);
66+
},
67+
];
68+
yield [
69+
'config.storage.sync',
70+
function (?DrupalServiceDefinition $service): void {
71+
self::assertNotNull($service);
72+
self::assertEquals('config.storage.staging', $service->getAlias());
73+
}
74+
];
75+
}
76+
77+
}

0 commit comments

Comments
 (0)