Skip to content
This repository was archived by the owner on Feb 6, 2020. It is now read-only.

Commit e35a8d1

Browse files
authored
Merge pull request #237 from fhein/TEST-Stability-Over-Internal-State-Changes
Adds test for issue #236
2 parents 3c9994a + cb5b49a commit e35a8d1

File tree

4 files changed

+221
-0
lines changed

4 files changed

+221
-0
lines changed

test/CommonServiceLocatorBehaviorsTrait.php

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
use function call_user_func_array;
3030
use function restore_error_handler;
3131
use function set_error_handler;
32+
use ZendTest\ServiceManager\TestAsset\SampleFactory;
33+
use ZendTest\ServiceManager\TestAsset\AbstractFactoryFoo;
34+
use ZendTest\ServiceManager\TestAsset\PassthroughDelegatorFactory;
3235

3336
trait CommonServiceLocatorBehaviorsTrait
3437
{
@@ -879,4 +882,154 @@ public function testCoverageDepthFirstTaggingOnRecursiveAliasDefinitions()
879882
$this->assertSame($sm->get('alias1'), $sm->get('alias2'));
880883
$this->assertSame($sm->get(stdClass::class), $sm->get('alias1'));
881884
}
885+
886+
/**
887+
* The ServiceManager can change internal state on calls to get,
888+
* build or has, latter not currently. Possible state changes
889+
* are caching a factory, registering a service produced by
890+
* a factory, ...
891+
*
892+
* This tests performs three consecutive calls to build/get for
893+
* each registered service to push the service manager through
894+
* all internal states, thereby verifying that build/get/has
895+
* remain stable through the internal states.
896+
*
897+
* @dataProvider provideConsistencyOverInternalStatesTests
898+
*
899+
* @param ContainerInterface $smTemplate
900+
* @param string $name
901+
* @param array[] string $test
902+
*/
903+
public function testConsistencyOverInternalStates($smTemplate, $name, $test, $shared)
904+
{
905+
$sm = clone $smTemplate;
906+
$object['get'] = [];
907+
$object['build'] = [];
908+
909+
// call get()/build() and store the retrieved
910+
// objects in $object['get'] or $object['build']
911+
// respectively
912+
foreach ($test as $method) {
913+
$obj = $sm->$method($name);
914+
$object[$shared ? $method : 'build'][] = $obj;
915+
$this->assertNotNull($obj);
916+
$this->assertTrue($sm->has($name));
917+
}
918+
919+
// compares the first to the first also, but ok
920+
foreach ($object['get'] as $sharedObj) {
921+
$this->assertSame($object['get'][0], $sharedObj);
922+
}
923+
// objects from object['build'] have to be different
924+
// from all other objects
925+
foreach ($object['build'] as $idx1 => $nonSharedObj1) {
926+
$this->assertNotContains($nonSharedObj1, $object['get']);
927+
foreach ($object['build'] as $idx2 => $nonSharedObj2) {
928+
if ($idx1 !== $idx2) {
929+
$this->assertNotSame($nonSharedObj1, $nonSharedObj2);
930+
}
931+
}
932+
}
933+
}
934+
935+
/**
936+
* Data provider
937+
*
938+
* @see testConsistencyOverInternalStates above
939+
*
940+
* @param ContainerInterface $smTemplate
941+
* @param string $name
942+
* @param string[] $test
943+
*/
944+
public function provideConsistencyOverInternalStatesTests()
945+
{
946+
$config1 = [
947+
'factories' => [
948+
// to allow build('service')
949+
'service' => function ($container, $requestedName, array $options = null) {
950+
return new stdClass();
951+
},
952+
'factory' => SampleFactory::class,
953+
'delegator' => SampleFactory::class,
954+
],
955+
'delegators' => [
956+
'delegator' => [
957+
PassthroughDelegatorFactory::class
958+
],
959+
],
960+
'invokables' => [
961+
'invokable' => InvokableObject::class,
962+
],
963+
'services' => [
964+
'service' => new stdClass(),
965+
],
966+
'aliases' => [
967+
'serviceAlias' => 'service',
968+
'invokableAlias' => 'invokable',
969+
'factoryAlias' => 'factory',
970+
'abstractFactoryAlias' => 'foo',
971+
'delegatorAlias' => 'delegator',
972+
],
973+
'abstract_factories' => [
974+
AbstractFactoryFoo::class
975+
]
976+
];
977+
$config2 = $config1;
978+
$config2['shared_by_default'] = false;
979+
980+
$configs = [ $config1, $config2 ];
981+
982+
foreach ($configs as $config) {
983+
$smTemplates[] = $this->createContainer($config);
984+
}
985+
986+
// produce all 3-tuples of 'build' and 'get', i.e.
987+
//
988+
// [['get', 'get', 'get'], ['get', 'get', 'build'], ...
989+
// ['build', 'build', 'build']]
990+
//
991+
$methods = ['get', 'build'];
992+
foreach ($methods as $method1) {
993+
foreach ($methods as $method2) {
994+
foreach ($methods as $method3) {
995+
$callSequences[] = [$method1, $method2, $method3];
996+
}
997+
}
998+
}
999+
1000+
foreach ($configs as $config) {
1001+
$smTemplate = $this->createContainer($config);
1002+
1003+
// setup sharing, services are always shared
1004+
$names = array_fill_keys(array_keys($config['services']), true);
1005+
1006+
// initialize the other keys with shared_by_default
1007+
// and merge them
1008+
$names = array_merge(array_fill_keys(array_keys(array_merge(
1009+
$config['factories'],
1010+
$config['invokables'],
1011+
$config['aliases'],
1012+
$config['delegators']
1013+
)), $config['shared_by_default'] ?? true), $names);
1014+
1015+
// add the key resolved by the abstract factory
1016+
$names['foo'] = $config['shared_by_default'] ?? true;
1017+
1018+
// adjust shared setting for individual keys from
1019+
// $shared array if present
1020+
if (! empty($config['shared'])) {
1021+
foreach ($config['shared'] as $name => $shared) {
1022+
$names[$name] = $shared;
1023+
}
1024+
}
1025+
1026+
foreach ($names as $name => $shared) {
1027+
foreach ($callSequences as $callSequence) {
1028+
$sm = clone $smTemplate;
1029+
$tests[] = [$smTemplate, $name, $callSequence, $shared];
1030+
}
1031+
}
1032+
}
1033+
return $tests;
1034+
}
8821035
}

test/TestAsset/AbstractFactoryFoo.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
/**
3+
* @link https://github.com/zendframework/zend-servicemanager for the canonical source repository
4+
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
5+
* @license http://framework.zend.com/license/new-bsd New BSD License
6+
*/
7+
8+
namespace ZendTest\ServiceManager\TestAsset;
9+
10+
use Interop\Container\ContainerInterface;
11+
use Zend\ServiceManager\Factory\AbstractFactoryInterface;
12+
13+
class AbstractFactoryFoo implements AbstractFactoryInterface
14+
{
15+
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
16+
{
17+
if ($requestedName === 'foo') {
18+
return new Foo($options);
19+
}
20+
return false;
21+
}
22+
23+
public function canCreate(ContainerInterface $container, $requestedName)
24+
{
25+
return ($requestedName === 'foo');
26+
}
27+
}

test/TestAsset/Foo.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
/**
3+
* @link https://github.com/zendframework/zend-servicemanager for the canonical source repository
4+
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
5+
* @license http://framework.zend.com/license/new-bsd New BSD License
6+
*/
7+
8+
namespace ZendTest\ServiceManager\TestAsset;
9+
10+
class Foo
11+
{
12+
protected $options;
13+
14+
public function __construct($options = null)
15+
{
16+
$this->options = $options;
17+
}
18+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
/**
3+
* @link https://github.com/zendframework/zend-servicemanager for the canonical source repository
4+
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (http://www.zend.com)
5+
* @license http://framework.zend.com/license/new-bsd New BSD License
6+
*/
7+
8+
namespace ZendTest\ServiceManager\TestAsset;
9+
10+
use Interop\Container\ContainerInterface;
11+
use Zend\ServiceManager\Factory\DelegatorFactoryInterface;
12+
13+
class PassthroughDelegatorFactory implements DelegatorFactoryInterface
14+
{
15+
/**
16+
* {@inheritDoc}
17+
* @see \Zend\ServiceManager\Factory\DelegatorFactoryInterface::__invoke()
18+
*/
19+
public function __invoke(ContainerInterface $container, $name, callable $callback, array $options = null)
20+
{
21+
return call_user_func($callback);
22+
}
23+
}

0 commit comments

Comments
 (0)