From c7fc888f940f1f0de54883b45ab72c98ef058d43 Mon Sep 17 00:00:00 2001 From: David Matejka Date: Wed, 13 Aug 2014 23:00:11 +0200 Subject: [PATCH] init --- .gitignore | 7 + composer.json | 29 ++++ src/IProcessor.php | 18 +++ src/Processors/InjectProcessor.php | 22 +++ src/Processors/SetupProcessor.php | 36 +++++ src/Processors/TagsProcessor.php | 33 +++++ src/SetupExtension.php | 125 ++++++++++++++++++ .../Setup/CompilerExtensionTestCase.php | 36 +++++ .../Setup/ConfiguratorTestCase.phpt | 41 ++++++ .../Setup/InjectProcessorTestCase.phpt | 38 ++++++ .../Setup/SetupProcessorTestCase.phpt | 66 +++++++++ .../Setup/TagProcessorTestCase.phpt | 62 +++++++++ .../Setup/TypeDetectTestCase.phpt | 87 ++++++++++++ tests/LibretteTests/Setup/config/basic.neon | 16 +++ tests/LibretteTests/Setup/mocks.php | 77 +++++++++++ tests/LibretteTests/bootstrap.php | 23 ++++ tests/php.ini-unix | 0 tests/run-tests.sh | 40 ++++++ 18 files changed, 756 insertions(+) create mode 100644 .gitignore create mode 100644 composer.json create mode 100644 src/IProcessor.php create mode 100644 src/Processors/InjectProcessor.php create mode 100644 src/Processors/SetupProcessor.php create mode 100644 src/Processors/TagsProcessor.php create mode 100644 src/SetupExtension.php create mode 100644 tests/LibretteTests/Setup/CompilerExtensionTestCase.php create mode 100644 tests/LibretteTests/Setup/ConfiguratorTestCase.phpt create mode 100644 tests/LibretteTests/Setup/InjectProcessorTestCase.phpt create mode 100644 tests/LibretteTests/Setup/SetupProcessorTestCase.phpt create mode 100644 tests/LibretteTests/Setup/TagProcessorTestCase.phpt create mode 100644 tests/LibretteTests/Setup/TypeDetectTestCase.phpt create mode 100644 tests/LibretteTests/Setup/config/basic.neon create mode 100644 tests/LibretteTests/Setup/mocks.php create mode 100644 tests/LibretteTests/bootstrap.php create mode 100644 tests/php.ini-unix create mode 100755 tests/run-tests.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..732fcbf --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +vendor +tests/**/*.actual +tests/**/*.expected +tests/**/coverage.* +composer.lock +tests/tmp +!.gitkeep diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..2f18222 --- /dev/null +++ b/composer.json @@ -0,0 +1,29 @@ +{ + "name": "librette/setup", + "keywords": ["nette", "librette", "di"], + "license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"], + "authors": [ + { + "name": "David Matějka", + "homepage": "http://www.matej21.cz" + } + ], + "require": { + "nette/di": "~2.2@dev", + "nette/utils": "~2.2@dev", + "nette/reflection": "~2.2@dev" + }, + "require-dev": { + "nette/bootstrap": "~2.2@dev", + "nette/neon": "~2.2@dev", + "nette/php-generator": "~2.2@dev", + "nette/tester": "@dev", + "tracy/tracy": "~2.2@dev", + "mockery/mockery": "@stable" + }, + "autoload": { + "psr-4": { + "Librette\\Setup\\": "src/" + } + } +} diff --git a/src/IProcessor.php b/src/IProcessor.php new file mode 100644 index 0000000..0eb4dc3 --- /dev/null +++ b/src/IProcessor.php @@ -0,0 +1,18 @@ +setInject(TRUE); + } + } + +} diff --git a/src/Processors/SetupProcessor.php b/src/Processors/SetupProcessor.php new file mode 100644 index 0000000..f0371ef --- /dev/null +++ b/src/Processors/SetupProcessor.php @@ -0,0 +1,36 @@ +getEntity(); + }, $definition->getSetup()); + Validators::assert($methods, 'array'); + foreach ($methods as $setup) { + $method = $setup; + $args = array(); + if ($method instanceof Statement) { + $args = $method->arguments; + $method = $method->getEntity(); + } + if (!in_array($method, $currentSetupMethods)) { + $definition->addSetup($method, $args); + } + } + } + +} diff --git a/src/Processors/TagsProcessor.php b/src/Processors/TagsProcessor.php new file mode 100644 index 0000000..28d1a96 --- /dev/null +++ b/src/Processors/TagsProcessor.php @@ -0,0 +1,33 @@ +getTags(); + foreach ((array) $tags as $tag => $attrs) { + if (is_int($tag) && is_string($attrs)) { + $tag = $attrs; + $attrs = TRUE; + } + if (!isset($currentTags[$tag])) { + $definition->addTag($tag, $attrs); + } + } + } + +} diff --git a/src/SetupExtension.php b/src/SetupExtension.php new file mode 100644 index 0000000..8f30027 --- /dev/null +++ b/src/SetupExtension.php @@ -0,0 +1,125 @@ + array( + 'setup' => 'Librette\Setup\Processors\SetupProcessor', + 'inject' => 'Librette\Setup\Processors\InjectProcessor', + 'tags' => 'Librette\Setup\Processors\TagsProcessor', + ), + 'targets' => array(), + ); + + + public function beforeCompile() + { + $builder = $this->getContainerBuilder(); + $config = $this->getConfig($this->defaults); + $processors = $config['processors']; + /** @var IProcessor[] $initializedProcessors */ + $initializedProcessors = array(); + $targets = $this->getTargets($config); + /** @var ServiceDefinition $definition */ + foreach ($builder->getDefinitions() as $definition) { + if (!($class = $this->detectClass($definition))) { + continue; + } + $types = self::getAllClassTypes($class); + foreach ($targets as $target) { + if (count(array_intersect($target['type'], $types)) === 0) { + continue; + } + unset($target['type']); + foreach ($target as $processor => $args) { + if (is_int($processor)) { + $processor = $args; + $args = TRUE; + } + if (!isset($processors[$processor])) { + throw new \RuntimeException("Setup processor \"{$processor}\" not exists."); + } + if (!isset($initializedProcessors[$processor])) { + $initializedProcessors[$processor] = new $processors[$processor]($builder); + } + $initializedProcessors[$processor]->process($definition, $args); + } + } + } + + } + + + /** + * @param ServiceDefinition + * @return string|null + */ + protected static function detectClass(ServiceDefinition $def) + { + if ($def->getClass()) { + return $def->getClass(); + } elseif ($interface = $def->getImplement()) { + $rc = Reflection\ClassType::from($interface); + $method = $rc->hasMethod('create') ? 'create' : ($rc->hasMethod('get') ? 'get' : NULL); + if ($method === NULL) { + return NULL; + } + if (!($returnType = $rc->getMethod($method)->getAnnotation('return'))) { + return NULL; + } + + return Reflection\AnnotationsParser::expandClassName(preg_replace('#[|\s].*#', '', $returnType), $rc); + } + + return NULL; + } + + + /** + * @param string + * @return array + */ + protected static function getAllClassTypes($class) + { + $rc = Reflection\ClassType::from($class); + $classTypes = array_merge(array($class), class_parents($class), class_implements($class)); + if(PHP_VERSION_ID >= 50400) { + do { + $classTypes = array_merge($classTypes, $rc->getTraitNames()); + } while ($rc = $rc->getParentClass()); + } + + return array_map(function ($val) { + return ltrim(strtolower($val), '\\'); + }, $classTypes); + } + + + /** + * @param array + * @return array + */ + protected function getTargets($config) + { + $targets = $config['targets']; + unset($config['processors'], $config['targets']); + $targets = array_merge($targets, $config); + foreach ($targets as &$target) { + $target['type'] = array_map(function ($val) { + return ltrim(strtolower($val), '\\'); + }, (array) $target['type']); + } + + return $targets; + } +} diff --git a/tests/LibretteTests/Setup/CompilerExtensionTestCase.php b/tests/LibretteTests/Setup/CompilerExtensionTestCase.php new file mode 100644 index 0000000..43aed66 --- /dev/null +++ b/tests/LibretteTests/Setup/CompilerExtensionTestCase.php @@ -0,0 +1,36 @@ + array( + 'test' => 'LibretteTests\Setup\TestProcessor', + ), + 'targets' => $targets, + ); + $compiler = \Mockery::mock('Nette\DI\Compiler'); + $builder = new Nette\DI\ContainerBuilder(); + $compiler->shouldReceive('getContainerBuilder')->andReturn($builder); + $compiler->shouldReceive('getConfig')->andReturn(array('setup' => $config)); + $builder->addDefinition('foo')->setClass('LibretteTests\Setup\Foo'); + $builder->addDefinition('bar')->setClass('LibretteTests\Setup\Bar'); + $extension = new Librette\Setup\SetupExtension(); + $extension->setCompiler($compiler, 'setup'); + + return $extension; + + } + +} diff --git a/tests/LibretteTests/Setup/ConfiguratorTestCase.phpt b/tests/LibretteTests/Setup/ConfiguratorTestCase.phpt new file mode 100644 index 0000000..2076f0e --- /dev/null +++ b/tests/LibretteTests/Setup/ConfiguratorTestCase.phpt @@ -0,0 +1,41 @@ +defaultExtensions = array_intersect_key($configurator->defaultExtensions, array('extensions' => TRUE)); + $configurator->autowireExcludedClasses = array(); + $configurator->setTempDirectory(TEMP_DIR); + $configurator->addConfig(__DIR__ . '/config/basic.neon'); + + /** @var Nette\DI\Container $container */ + $container = $configurator->createContainer(); + /** @var Bar $bar */ + $bar = $container->getService('bar'); + Tester\Assert::same($container->getService('foo'), $bar->foo); + Tester\Assert::same($container->getService('lorem'), $bar->lorem); + } +} + + +\run(new ConfiguratorTestCase()); diff --git a/tests/LibretteTests/Setup/InjectProcessorTestCase.phpt b/tests/LibretteTests/Setup/InjectProcessorTestCase.phpt new file mode 100644 index 0000000..1be7a7e --- /dev/null +++ b/tests/LibretteTests/Setup/InjectProcessorTestCase.phpt @@ -0,0 +1,38 @@ +createExtension(array( + array( + 'type' => 'LibretteTests\Setup\Bar', + 'inject' => TRUE, + ) + )); + $builder = $extension->getContainerBuilder(); + $extension->beforeCompile(); + Tester\Assert::false($builder->getDefinition('foo')->getInject()); + Tester\Assert::true($builder->getDefinition('bar')->getInject()); + } +} + + +\run(new InjectProcessorTestCase()); diff --git a/tests/LibretteTests/Setup/SetupProcessorTestCase.phpt b/tests/LibretteTests/Setup/SetupProcessorTestCase.phpt new file mode 100644 index 0000000..03743de --- /dev/null +++ b/tests/LibretteTests/Setup/SetupProcessorTestCase.phpt @@ -0,0 +1,66 @@ +createExtension(array( + array( + 'type' => 'LibretteTests\Setup\Foo', + 'setup' => array( + 'injectFoo', + new Nette\DI\Statement('setBar', array('value')), + ), + ) + )); + $builder = $extension->getContainerBuilder(); + + $extension->beforeCompile(); + $setup = $builder->getDefinition('foo')->getSetup(); + Tester\Assert::count(2, $setup); + Tester\Assert::equal(new Nette\DI\Statement('injectFoo', array()), $setup[0]); + Tester\Assert::equal(new Nette\DI\Statement('setBar', array('value')), $setup[1]); + } + + + public function testNotRewriting() + { + $extension = $this->createExtension(array( + array( + 'type' => 'LibretteTests\Setup\Foo', + 'setup' => array( + new Nette\DI\Statement('setBar', array('value')), + ), + ) + )); + $builder = $extension->getContainerBuilder(); + $definition = $builder->getDefinition('foo'); + $definition->addSetup('setBar', array('lorem')); + + $extension->beforeCompile(); + $setup = $definition->getSetup(); + Tester\Assert::count(1, $setup); + Tester\Assert::equal(new Nette\DI\Statement('setBar', array('lorem')), $setup[0]); + } + +} + + +\run(new SetupProcessorTestCase()); diff --git a/tests/LibretteTests/Setup/TagProcessorTestCase.phpt b/tests/LibretteTests/Setup/TagProcessorTestCase.phpt new file mode 100644 index 0000000..8ff570f --- /dev/null +++ b/tests/LibretteTests/Setup/TagProcessorTestCase.phpt @@ -0,0 +1,62 @@ +createExtension(array( + array( + 'type' => 'LibretteTests\Setup\Foo', + 'tags' => $tags, + ) + )); + $builder = $extension->getContainerBuilder(); + + $extension->beforeCompile(); + $tags = $builder->getDefinition('foo')->getTags(); + Tester\Assert::count(count($expected), $tags); + Tester\Assert::equal($expected, $tags); + } + + + public function loadData() + { + return + array( + array( + 'foo', + array('foo' => TRUE) + ), + array( + array('foo', 'bar' => 'attr'), + array('foo' => TRUE, 'bar' => 'attr'), + ), + + ); + } +} + + +\run(new TagProcessorTestCase()); diff --git a/tests/LibretteTests/Setup/TypeDetectTestCase.phpt b/tests/LibretteTests/Setup/TypeDetectTestCase.phpt new file mode 100644 index 0000000..ade14fb --- /dev/null +++ b/tests/LibretteTests/Setup/TypeDetectTestCase.phpt @@ -0,0 +1,87 @@ +createExtension($config); + $builder = $extension->getContainerBuilder(); + Assert::count(0, TestProcessor::$passed); + $extension->beforeCompile(); + Assert::count(count($expected), TestProcessor::$passed); + foreach ($expected as &$value) { + $value[0] = $builder->getDefinition($value[0]); + } + Assert::equal($expected, TestProcessor::$passed); + TestProcessor::$passed = array(); + } + + + public function testFactory() + { + $extension = $this->createExtension(array(array('type' => 'LibretteTests\Setup\Bar', 'test' => 'target'))); + $builder = $extension->getContainerBuilder(); + $builder->removeDefinition('foo'); + $builder->removeDefinition('bar'); + $builder->addDefinition('barFactory')->setImplement('LibretteTests\Setup\BarFactory'); + Assert::count(0, TestProcessor::$passed); + $extension->beforeCompile(); + Assert::count(1, TestProcessor::$passed); + Assert::equal(array($builder->getDefinition('barFactory'), 'target'), TestProcessor::$passed[0]); + } + + + protected function loadData() + { + $data = array(); + $data[] = array( + array( + array('type' => 'LibretteTests\Setup\Foo', 'test' => 'target1'), + array('type' => 'LibretteTests\Setup\Bar', 'test' => 'target2'), + ), + array(array('foo', 'target1'), array('bar', 'target1'), array('bar', 'target2')), + ); + $data[] = array( + array( + array('type' => 'LibretteTests\Setup\FooInterface', 'test' => 'target1'), + ), + array(array('foo', 'target1'), array('bar', 'target1')), + ); + if (PHP_VERSION_ID >= 50400) { + $data[] = array( + array( + array('type' => 'LibretteTests\Setup\FooTrait', 'test' => 'target1'), + ), + array(array('foo', 'target1'), array('bar', 'target1')), + ); + } + + return $data; + } +} + + +\run(new TypeDetectTestCase()); diff --git a/tests/LibretteTests/Setup/config/basic.neon b/tests/LibretteTests/Setup/config/basic.neon new file mode 100644 index 0000000..d221761 --- /dev/null +++ b/tests/LibretteTests/Setup/config/basic.neon @@ -0,0 +1,16 @@ +extensions: + setup: Librette\Setup\SetupExtension + +services: + bar: + class: LibretteTests\Setup\Bar + autowired: false + foo: LibretteTests\Setup\Foo + lorem: LibretteTests\Setup\Lorem +setup: + - + type: [LibretteTests\Setup\Foo] + inject: true + setup: + - setFoo(@foo) + tags: ['xx', 'yy'] diff --git a/tests/LibretteTests/Setup/mocks.php b/tests/LibretteTests/Setup/mocks.php new file mode 100644 index 0000000..3d0ed46 --- /dev/null +++ b/tests/LibretteTests/Setup/mocks.php @@ -0,0 +1,77 @@ += 50400) { + trait FooTrait + { + + } + + + class Foo implements FooInterface + { + + use FooTrait; + } + +} else { + class Foo implements FooInterface + { + + } +} + + +class Bar extends Foo +{ + + /** @var Lorem @inject */ + public $lorem; + + /** @var Lorem */ + public $foo; + + + public function setFoo(Foo $foo) + { + $this->foo = $foo; + } +} + + +interface BarFactory +{ + + /** + * @return Bar + */ + public function create(); +} + + +class Lorem +{ + +} diff --git a/tests/LibretteTests/bootstrap.php b/tests/LibretteTests/bootstrap.php new file mode 100644 index 0000000..c5438e9 --- /dev/null +++ b/tests/LibretteTests/bootstrap.php @@ -0,0 +1,23 @@ +add('LibretteTests', __DIR__ . '/../'); + +Tracy\Debugger::enable(Tracy\Debugger::DEVELOPMENT, __DIR__ . '/../tmp/'); +Tester\Environment::setup(); +date_default_timezone_set('Europe/Prague'); +define('TEMP_DIR', __DIR__ . '/../tmp/' . (isset($_SERVER['argv']) ? md5(serialize($_SERVER['argv'])) : getmypid())); +Tester\Helpers::purge(TEMP_DIR); + + +$_SERVER = array_intersect_key($_SERVER, array_flip(array('PHP_SELF', 'SCRIPT_NAME', 'SERVER_ADDR', 'SERVER_SOFTWARE', 'HTTP_HOST', 'DOCUMENT_ROOT', 'OS', 'argc', 'argv'))); +$_SERVER['REQUEST_TIME'] = 1234567890; +$_ENV = $_GET = $_POST = array(); + +function run(Tester\TestCase $testCase) { + $testCase->run(isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : NULL); +} diff --git a/tests/php.ini-unix b/tests/php.ini-unix new file mode 100644 index 0000000..e69de29 diff --git a/tests/run-tests.sh b/tests/run-tests.sh new file mode 100755 index 0000000..b91d57a --- /dev/null +++ b/tests/run-tests.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +# Path to this script's directory +dir=$(cd `dirname $0` && pwd) + +# Path to test runner script +runnerScript="$dir/../vendor/nette/tester/Tester/tester.php" +if [ ! -f "$runnerScript" ]; then + echo "Nette Tester is missing. You can install it using Composer:" >&2 + echo "php composer.phar update --dev." >&2 + exit 2 +fi + +# Path to php.ini if passed as argument option +phpIni= +while getopts ":c:" opt; do + case $opt in + c) phpIni="$OPTARG" + ;; + + :) echo "Missing argument for -$OPTARG option" >&2 + exit 2 + ;; + esac +done + +# Runs tests with script's arguments, add default php.ini if not specified +# Doubled -c option intentionally +if [ -n "$phpIni" ]; then + php -c "$phpIni" "$runnerScript" -j 20 "$@" +else + php -c "$dir/php.ini-unix" "$runnerScript" -j 20 -c "$dir/php.ini-unix" "$@" +fi +error=$? + +# Print *.actual content if tests failed +if [ "${VERBOSE-false}" != "false" -a $error -ne 0 ]; then + for i in $(find . -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done + exit $error +fi \ No newline at end of file