From ae1bae93e19c7dedf835e8bb7519f4cd55f72f82 Mon Sep 17 00:00:00 2001 From: Augusto Pascutti Date: Sun, 3 Dec 2017 08:12:36 -0200 Subject: [PATCH 1/2] Updates ContainerInterop to use PSR-11 (breaks BC) This is a breaking change from the previous version, as the [ContainerInterop][1] interfaces are no longer implemented. As of their recommendation, but not because of it :wink:, we are now conforming the [PSR-11][2] interfaces. Not all interfaces were implemented (:eyes: Exceptions), so start using them - conforming with previous exceptions thrown by Respect\Config. Change 95ab990 modified an `\InvalidArgumentException` to an `Exception` type, this reverts that change to conform with previous contracts. Rather than testing interoperability inside `ContainerTest` test case, extract those tests into their own test case under a more friendly namespace (`Test\Feature`) and better test names - along with the test for the exception bullshit. Technical debts --------------- The test suite now holds two different organizations, one (introduced here) unfinished. I've already dived into this code base a few times and tests helped me a lot, but having them matching their concrete classes might not be the best organization for that. As splitting this suite into groups like "unit" and "integration" may not as useful as it is inside a huge application (with dozens of dependencies), we can use something like: "feature", "issues" and "library". [1]: https://github.com/container-interop/container-interop [2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md --- composer.json | 4 +- library/Respect/Config/Container.php | 4 +- .../Config/InvalidArgumentException.php | 10 ++ library/Respect/Config/NotFoundException.php | 7 +- phpunit.xml.dist | 7 +- tests/feature/Psr11.php | 106 ++++++++++++++++++ .../library/Respect/Config/ContainerTest.php | 27 ----- 7 files changed, 129 insertions(+), 36 deletions(-) create mode 100644 library/Respect/Config/InvalidArgumentException.php create mode 100644 tests/feature/Psr11.php diff --git a/composer.json b/composer.json index 432743e..5e52984 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "type": "library", "homepage": "https://github.com/Respect/Config", "description": "A powerful, small, deadly simple configurator and dependency injection container made to be easy.", - "keywords": ["respect", "config", "dic", "dependency injection"], + "keywords": ["respect", "config", "dic", "dependency injection", "psr-11", "container"], "license": "BSD-3-Clause", "authors": [ { @@ -18,7 +18,7 @@ }, "require": { "php": ">=5.3.0", - "container-interop/container-interop": "^1.1" + "psr/container": "^1.0" }, "require-dev": { "phpunit/phpunit": "~4.4", diff --git a/library/Respect/Config/Container.php b/library/Respect/Config/Container.php index f74c580..1bd377c 100644 --- a/library/Respect/Config/Container.php +++ b/library/Respect/Config/Container.php @@ -2,11 +2,11 @@ namespace Respect\Config; -use InvalidArgumentException as Argument; +use Respect\Config\InvalidArgumentException as Argument; use ArrayObject; use ReflectionClass; use ReflectionFunction; -use Interop\Container\ContainerInterface; +use Psr\Container\ContainerInterface; class Container extends ArrayObject implements ContainerInterface { diff --git a/library/Respect/Config/InvalidArgumentException.php b/library/Respect/Config/InvalidArgumentException.php new file mode 100644 index 0000000..839f0d7 --- /dev/null +++ b/library/Respect/Config/InvalidArgumentException.php @@ -0,0 +1,10 @@ + - - tests + + tests/library + + + tests/feature diff --git a/tests/feature/Psr11.php b/tests/feature/Psr11.php new file mode 100644 index 0000000..ba1c7fe --- /dev/null +++ b/tests/feature/Psr11.php @@ -0,0 +1,106 @@ +loadArray(parse_ini_string($ini, true)); + + $this->assertTrue( + $c->has('foo'), + 'Searching an existing item must succeed.' + ); + $this->assertEquals( + 'bar', + $c->get('foo'), + 'Retrieving an existing item must succeed.' + ); + $this->assertEquals( + 'bat', + $c->get('baz'), + 'Retrieving an existing item must succeed.' + ); + } + + /** + * @expectedException Psr\Container\NotFoundExceptionInterface + * @expectedExceptionMessage Item baz not found + */ + public function testItemNotFoundInContainerMatchesInteropException() + { + $this->loadInvalidItem(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Item baz not found + */ + public function testItemNotFoundInContainerMatchesPreviousRespectConfigException() + { + $this->loadInvalidItem(); + } + + /** + * @expectedException Respect\Config\InvalidArgumentException + * @expectedExceptionMessage Item baz not found + */ + public function testItemNotFoundInContainerMatchesCustomRespectException() + { + $this->loadInvalidItem(); + } + + private function loadInvalidItem() + { + $ini = <<loadArray(parse_ini_string($ini, true)); + $c->get('baz'); + } + + /** + * @expectedException Psr\Container\ContainerExceptionInterface + */ + public function testContainerExceptionMatchesInteropException() + { + $this->parseInvalidIni(); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testContainerExceptionMatchesPreviousRespectConfigException() + { + $this->parseInvalidIni(); + } + + /** + * @expectedException Respect\Config\InvalidArgumentException + */ + public function testContainerExceptionUsesCustomRespectException() + { + $this->parseInvalidIni(); + } + + private function parseInvalidIni() + { + $c = new Container(14); + $c->foo; + } +} + diff --git a/tests/library/Respect/Config/ContainerTest.php b/tests/library/Respect/Config/ContainerTest.php index 81c3417..e657277 100644 --- a/tests/library/Respect/Config/ContainerTest.php +++ b/tests/library/Respect/Config/ContainerTest.php @@ -48,33 +48,6 @@ public function testLoadFile() $this->assertEquals('bar', $c->getItem('foo')); $this->assertEquals('bat', $c->getItem('baz')); } - - public function testContainerInterop() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $this->assertTrue($c->has('foo')); - $this->assertEquals('bar', $c->get('foo')); - $this->assertEquals('bat', $c->get('baz')); - } - - /** - * @expectedException Interop\Container\Exception\NotFoundException - * @expectedExceptionMessage Item baz not found - */ - public function testLoadInvalidName() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $c->get('baz'); - } /** * @expectedException InvalidArgumentException From 052676a9043e3597a3b487d113274cb7429ac2a5 Mon Sep 17 00:00:00 2001 From: Augusto Pascutti Date: Sun, 3 Dec 2017 14:37:13 -0200 Subject: [PATCH 2/2] Explain tests and organize them Library tests are now separated from "feature" tests, which explain better what each feature of the component is and how it works. Works better than a README if you ask me, or Respect\Doc for that matter - which suffered an overhaul that I still haven't take the time to understand. Previous coverage is unchanged, although more tests were added for clarity and better coverage of each feature usage. Changes ------- You can now indent INI declarations, every blank space before the start of any declaration will be removed. A bug happened when you manipulated the container by defining an item entry with an instance. The "ifs" surrounding `$this->configure()` were unnecessary. Describes development dependencies better on `composer.json` to run the test suite. --- composer.json | 2 + library/Respect/Config/Container.php | 25 +- tests/feature/Callback.php | 98 ++++ tests/feature/ContainerAsArray.php | 45 ++ tests/feature/ContainerManipulation.php | 92 ++++ tests/feature/IniDsl/ConstantExpansion.php | 108 +++++ tests/feature/IniDsl/Instantiation.php | 347 ++++++++++++++ tests/feature/IniDsl/Sequence.php | 64 +++ tests/feature/IniDsl/VariableExpansion.php | 126 +++++ tests/feature/LazyLoading.php | 200 ++++++++ .../library/Respect/Config/ContainerTest.php | 443 ++---------------- .../Issues/EnviromentConfigurationTest.php | 28 -- .../Issues/InstantiatorImplementsInvoke.php | 76 --- .../Respect/Config/Issues/LazyLoadTest.php | 76 --- .../Config/Issues/StaticFactoryTest.php | 27 -- 15 files changed, 1121 insertions(+), 636 deletions(-) create mode 100644 tests/feature/Callback.php create mode 100644 tests/feature/ContainerAsArray.php create mode 100644 tests/feature/ContainerManipulation.php create mode 100644 tests/feature/IniDsl/ConstantExpansion.php create mode 100644 tests/feature/IniDsl/Instantiation.php create mode 100644 tests/feature/IniDsl/Sequence.php create mode 100644 tests/feature/IniDsl/VariableExpansion.php create mode 100644 tests/feature/LazyLoading.php delete mode 100644 tests/library/Respect/Config/Issues/EnviromentConfigurationTest.php delete mode 100644 tests/library/Respect/Config/Issues/InstantiatorImplementsInvoke.php delete mode 100644 tests/library/Respect/Config/Issues/LazyLoadTest.php delete mode 100644 tests/library/Respect/Config/Issues/StaticFactoryTest.php diff --git a/composer.json b/composer.json index 5e52984..6b936c2 100644 --- a/composer.json +++ b/composer.json @@ -21,6 +21,8 @@ "psr/container": "^1.0" }, "require-dev": { + "ext-PDO": "*", + "ext-pdo_sqlite": "*", "phpunit/phpunit": "~4.4", "respect/test": "dev-master" }, diff --git a/library/Respect/Config/Container.php b/library/Respect/Config/Container.php index 1bd377c..8d80f2a 100644 --- a/library/Respect/Config/Container.php +++ b/library/Respect/Config/Container.php @@ -21,12 +21,10 @@ public function __isset($name) { return $this->has($name); } - - public function has($name) + + public function has($name) { - if ($this->configurator) { - $this->configure(); - } + $this->configure(); return parent::offsetExists($name); } @@ -70,9 +68,7 @@ function ($param) use ($container) { parent::offsetSet($name, $item); } - if ($this->configurator) { - $this->configure(); - } + $this->configure(); return $this; } @@ -107,12 +103,10 @@ protected function configure() throw new Argument("Invalid input. Must be a valid file or array"); } - + public function getItem($name, $raw = false) { - if ($this->configurator) { - $this->configure(); - } + $this->configure(); if (!isset($this[$name])) { throw new NotFoundException("Item $name not found"); @@ -124,7 +118,7 @@ public function getItem($name, $raw = false) return $this->lazyLoad($name); } - + public function get($name) { return $this->getItem($name); @@ -132,6 +126,7 @@ public function get($name) public function loadString($configurator) { + $configurator = preg_replace('/^[\s\t]+/', '', $configurator); $iniData = parse_ini_string($configurator, true); if (false === $iniData || count($iniData) == 0) { throw new Argument("Invalid configuration string"); @@ -268,6 +263,10 @@ protected function hasCompleteBrackets($value) protected function parseSingleValue($value) { + if (is_object($value)) { + return $value; + } + $value = trim($value); if ($this->hasCompleteBrackets($value)) { return $this->parseBrackets($value); diff --git a/tests/feature/Callback.php b/tests/feature/Callback.php new file mode 100644 index 0000000..b18361b --- /dev/null +++ b/tests/feature/Callback.php @@ -0,0 +1,98 @@ + 'Hello'); + $result = $c($definition); + + $this->assertEquals( + 'Hello', + $result->bar, + 'Calling the container as a function will append the array passed as content to it.' . PHP_EOL . + 'It will return the itself, as well.' + ); + $this->assertSame( + $result->bar, + $c->bar, + "But it doesn't matter on which instance of the container you call." + ); + } + + public function testLateDefinitionOfVariableExpansionThroughItemCallbackReturnsValue() + { + $c = new Container(<<bar(array('undef'=>'Hello')); + $this->assertEquals('Hello', $result); + } + + public function testRetrievalOfItemThroughInstanceTypeOnContainerCallbackReturnsValue() + { + $called = false; + $c = new Container(<<assertInstanceOf('DateTime', $result); + $this->assertInstanceOf('DateTime', $result2); + $this->assertTrue($called); + } + + public function testRetrievalOfInstanceTypeThroughContainerCallbackReturnsValueEvenWithoutDeclaringItsType() + { + $c = new Container(); + $c(new \DateTime); + $called = false; + + $result = $c(function(\DateTime $date) use (&$called) { + $called = true; + return $date; + }); + + $result2 = $c['DateTime']; + $this->assertInstanceOf('DateTime', $result); + $this->assertInstanceOf('DateTime', $result2); + $this->assertTrue($called); + } + + public function testContainerCallbackReceivingACallableCallsItAndReturnsValue() + { + $c = new Container(); + $c(new \DateTime); + $result = $c(array('Test\Stub\TimePrinter', 'returnTimePassedAsArgument')); + $this->assertInstanceOf('DateTime', $result); + } +} + +namespace Test\Stub; + +class TimePrinter +{ + public function returnTimePassedAsArgument(\DateTime $time) + { + return $time; + } +} + diff --git a/tests/feature/ContainerAsArray.php b/tests/feature/ContainerAsArray.php new file mode 100644 index 0000000..33db0de --- /dev/null +++ b/tests/feature/ContainerAsArray.php @@ -0,0 +1,45 @@ +loadString(<<assertInstanceOf( + 'ArrayAccess', + $c, + 'The container implements the \ArrayAccess interface, so it behaves like an array.' + ); + $this->assertTrue( + isset($c['fibonacci']) + ); + $this->assertEquals( + array(1, 1, 2, 3, 5), + $c['fibonacci'], + 'The container implements the \ArrayAccess interface, so it behaves like an array.' + ); + } + + public function testDefiningItemWithArrayLikeNotation() + { + $c = new Container; + + $c['not'] = false; + $this->assertTrue(isset($c['not'])); + $this->assertFalse($c['not']); + } +} + diff --git a/tests/feature/ContainerManipulation.php b/tests/feature/ContainerManipulation.php new file mode 100644 index 0000000..12b866b --- /dev/null +++ b/tests/feature/ContainerManipulation.php @@ -0,0 +1,92 @@ +name = 'John Snow'; + + $this->assertEquals( + 'John Snow', + $c->name, + 'You can define new items just like you would define a property on an instance.' + ); + } + + public function testDefinitionOfItemWithAnonymousFunctionExecutesItAndReturnsItsValueUponUsage() + { + $c = new Container; + + $c->name = function() { return 'John Doe'; }; + + $this->assertEquals( + 'John Doe', + $c->name, + 'The function gets executed and the return value is stored in the container.' + ); + } + + public function testDefinitionOfItemOnContainerWithItems() + { + $c = new Container(<<panda = function() { return 'ok'; }; + + $this->assertEquals( + 'ok', + $c->panda, + 'It works if the container has stuff or not.' + ); + } + + /** + * @group issue + * @ticket 14 + */ + public function testItemOverwrrite() + { + $c = new Container(<<pdo = new \PDO('sqlite::memory:'); + + $this->assertNotInstanceOf( + 'StdClass', + $c->pdo, + 'Although PDO was defined with StdClass we overwritten it to a proper instance of PDO manually.' + ); + + $this->assertSame( + $c->pdo, + $c->db->c, + 'This should overwrite every usage of that element inside the container.' + ); + } +} + +namespace Test\Stub; + +class DatabaseWow +{ + public $c; + + public function __construct($con) + { + $this->c = $con; + } +} + diff --git a/tests/feature/IniDsl/ConstantExpansion.php b/tests/feature/IniDsl/ConstantExpansion.php new file mode 100644 index 0000000..f2d5a30 --- /dev/null +++ b/tests/feature/IniDsl/ConstantExpansion.php @@ -0,0 +1,108 @@ +assertEquals( + E_USER_ERROR, + $c->error_reporting, + 'Constants are always UPPER CASE, the one being used is already declared by PHP.' + ); + } + + public function testConstantDeclaredBeforeUsageGetsExpandedCorrectly() + { + $c = new Container(<<assertEquals( + MY_SWEET_CONSTANT_FOR_TESTING, + $c->expansion_happens_on_usage, + 'Constant expansion happens on first usage, so declaring constants after INI parsing is fine.' + ); + } + + public function testNonExistingConstantUsageExpandsToItsName() + { + $c = new Container(<<assertEquals( + 'NON_EXISTING_CONSTANT', + $c->test_value, + 'Non existing constants are expanded to their name, mimicing PHP behavior.' + ); + } + + public function testClassConstantExpansionInsideASequence() + { + $c = new Container(<<assertEquals( + array(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION), + $c->pdo_error_mode, + 'Class constants also get expanded! Every feature (e.g: sequences) still works.' + ); + } + + public function testNonExistingClassConstantExpandsToItsName() + { + $c = new Container(<<assertEquals( + 'PDO::ERRMODE', + $c->pdo_error_mode, + 'Non existing class constants get expanded to their full name when used.' + ); + } + + public function testNonExistingClassConstantInsideASequenceExpandsToItsName() + { + $c = new Container(<<assertEquals( + array('PDO::ERRMODE2', \PDO::ERRMODE_EXCEPTION), + $c->pdo_error_mode, + 'Class constants also get expanded to their name while inside a sequence.' + ); + } + + public function testConstantExpansionStringInterpolation() + { + $c = new Container(<<assertEquals( + "/var/log", + $c->file_system_path, + 'Constants can be used with string values as well.' + ); + } +} diff --git a/tests/feature/IniDsl/Instantiation.php b/tests/feature/IniDsl/Instantiation.php new file mode 100644 index 0000000..c0d82eb --- /dev/null +++ b/tests/feature/IniDsl/Instantiation.php @@ -0,0 +1,347 @@ +assertInstanceOf( + 'StdClass', + $c->foo, + 'INI sections with space in their names create new instances: ' . PHP_EOL . + ' * The string before the space is the instance name' . PHP_EOL . + ' * The string after the space is the instance type' + ); + } + + public function testInstanceNameWithUnderline() + { + $c = new Container(<<assertInstanceOf( + 'StdClass', + $c->foo_bar, + 'Instances with underline in their names should work as expected.' + ); + } + + public function testCreatingAnInstanceOfStdclassWithinAValueName() + { + $c = new Container(<<assertInstanceOf( + 'StdClass', + $c->foo, + 'INI value names can also trigger class instantiation, we prefer the section-way though.' + ); + } + + public function testInstancePropertyDefinition() + { + $c = new Container(<<assertInstanceOf( + 'StdClass', + $c->person, + 'Every item in the section is treated as a public property and, therefore, defined in the instance.' + ); + $this->assertEquals( + 'Rhaegar Targaryen', + $c->person->father, + 'Did you guess that in the show?' + ); + $this->assertEquals( + 'John Snow', + $c->person->name, + 'Most boring character. Ever.' + ); + $this->assertEquals( + 'Lyanna Stark', + $c->person->mother, + 'Feel like talking about *that* butterfly effect?' + ); + } + + public function testInstancePropertyDefinitionWithExpansionOfAnotherInstance() + { + $c = new Container(<<assertEquals( + get_class($c->foo_bar), + get_class($c->bar_foo->test) + ); + } + + + public function testInstancePropertyOfTypeArray() + { + $ini = <<loadArray(parse_ini_string($ini, true)); + $instantiator = $c->getItem('foo', true); + $expected = array( + 'abc' => 'bar', + 'def' => 'bat' + ); + $this->assertEquals($expected, $instantiator->getParam('foo')); + } + + public function testPropertyDefinitionWithSequences() + { + $ini = <<loadArray(parse_ini_string($ini, true)); + $instantiator = $c->getItem('foo', true); + $expectedFoo = array( + 'abc' => array('bat', 'blz'), + 'def' => 'bat' + ); + $expectedBaz = array('bat', 'blz'); + $this->assertEquals($expectedFoo, $instantiator->getParam('foo')); + $this->assertEquals($expectedBaz, $instantiator->getParam('baz')); + } + + public function testPropertyDefinitionWithVariableExpansion() + { + $ini = <<loadArray(parse_ini_string($ini, true)); + $instantiator = $c->getItem('foo', true); + $expectedFoo = array( + 'abc' => array('bat', 'blz'), + 'def' => 'bat' + ); + $expectedBaz = array('bat', 'someName'); + $this->assertEquals($expectedFoo, $instantiator->getParam('foo')); + $this->assertEquals($expectedBaz, $instantiator->getParam('baz')); + } + + public function testUsingTheSameInstanceTwiceReusesThePreviouslyCreatedInstances() + { + $c = new Container(<<now; + $this->assertInstanceOf( + 'DateTime', + $firstValue, + 'Instance type given should be respected.' + ); + $this->assertSame( + $firstValue, + $c->now, + 'After instantiation, the instance remains the same. No matter how many times you use it.' + ); + } + + public function testNewInstanceWithConstructorArgument() + { + $c = new Container(<<assertInstanceOf( + 'DateTimeZone', + $c->home, + 'Constructor parameters can be defined as a sequence (array) passed as value to the "__construct".' + ); + $this->assertEquals( + 'America/Sao_Paulo', + $c->home->getName(), + 'Instances work like in any other place, you can call methods on them later...' + ); + } + + /** + * @group issue + * @ticket 40 + */ + public function testInstanceExpansionWithTypeConstructorParameter() + { + $c = new Container(<<assertInstanceOf( + 'Test\Stub\TypeHintWowMuchType', + $c->typed, + 'That object requires a DateTime as constructor argument.' + ); + } + + /** + * @group issue + * @ticket 9 + */ + public function testInstanceThroughStaticFactoryAlwaysCreateNewObjects() + { + $c = new Container(<<y2k; + $secondCall = $c->y2k; + $this->assertInstanceOf( + 'DateTime', + $firstCall, + 'Static factory behavior is to create an instance after calling a method.' . PHP_EOL . + 'When you define an instance as INI section and pass it a method name (which is always' . PHP_EOL . + 'followed by brackets), we will call "DateTime::createFromFormat" to retrieve the instance.' + ); + $this->assertSame( + $firstCall, + $secondCall, + 'Even through we are creating instances from a method call, after the first instantiation the' . PHP_EOL . + 'instance is not changed. No matter how many times you use it.' + ); + } + + public function testInstatiationOfNewObjectEveryTimeItIsUsed() + { + $c = new Container(<<assertInstanceOf( + 'DateTime', + $c->now, + 'Instance type should always be respected.' + ); + $this->assertNotSame( + $c->now, + $c->now, + 'When "new" is used before declaring the type of instance, we will always create new instances.' + ); + } + + public function testCallingMethodWithValueOnAnInstanceMultipleTimes() + { + $c = new Container(<<assertEquals( + 'one', + $c->day->dequeue(), + 'Queue was created and two items were enqueued.' + ); + $this->assertEquals( + 'two', + $c->day->dequeue(), + 'Methods are called in the order they are defined inside the section.' + ); + } + + public function testCallingMethodWithoutValue() + { + $c = new Container(<<assertNotEmpty( + $c->db->query('SELECT * FROM sqlite_master')->fetch() + ); + } + + public function testInstanceOfDefinesTheInstanceNameAsItsOwnInstance() + { + $c = new Container(<<assertInstanceOf( + 'DateTime', + $c->DateTime, + 'Although the "DateTime" item is not defined, the `instanceof` operator registers it.' + ); + $this->assertSame( + $c->DateTime, + $c->person->birthday, + 'You can use this kind of thing when you full class name is a nice one, and a single instance is relavant.' + ); + } +} + +namespace Test\Stub; + +class WheneverWithAProperty +{ + public $test; +} + +class TypeHintWowMuchType +{ + public $d; + + public function __construct(\DateTime $date) + { + $this->d = $date; + } +} + diff --git a/tests/feature/IniDsl/Sequence.php b/tests/feature/IniDsl/Sequence.php new file mode 100644 index 0000000..3262708 --- /dev/null +++ b/tests/feature/IniDsl/Sequence.php @@ -0,0 +1,64 @@ +assertEquals( + array(1, 1, 2, 3, 5), + $c->fibonacci, + 'Sequences are arrays declared much like in PHP (after 5.4 introduction of short array syntax).' + ); + } + + public function testSectionSequenceDeclarationTranslatesToArrayUponUsage() + { + $c = new Container(<<assertEquals( + array(1, 1, 2, 3, 5), + $c->fibonacci, + 'INI sections are also sequences, where the name is the index of the resulting array.' + ); + } + + public function testSectionSequenceDeclarationUsingAssociativeArrayIndexes() + { + $c = new Container(<<assertEquals( + array( + "Config" => "unstable", + "Rest" => "stable", + "Template" => "experimental", + "Foundation" => "deprecated" + ), + $c->components, + 'Sequences declared as INI sections can also use associative indexes.' + ); + } +} diff --git a/tests/feature/IniDsl/VariableExpansion.php b/tests/feature/IniDsl/VariableExpansion.php new file mode 100644 index 0000000..85451af --- /dev/null +++ b/tests/feature/IniDsl/VariableExpansion.php @@ -0,0 +1,126 @@ +assertEquals( + 'John Snow', + $c->getItem('who_knows_nothing'), + "INI values between brackets should be treated as variables and suffer expansion." + ); + } + + public function testVariableExpansionUsingStringInterpolation() + { + $c = new Container(<<assertEquals( + 'mysql:host=localhost;dbname=my_database', + $c->getItem('db_dsn'), + 'Variables can be used inside a INI value multiple times.' + ); + } + + public function testExpansionOfNonExistingVariableDoesNotFailParsing() + { + $c = new Container(<<who_knows_nothing; + } + + /** + * @expectedException Respect\Config\NotFoundException + * @expectedExceptionMessage Item my_name not found + */ + public function testExpansionAndUsageOfAnEmptyValueThrowsException() + { + $c = new Container(<<who_knows_nothing; + } + + /** + * @expectedException Respect\Config\NotFoundException + * @expectedExceptionMessage Item my_name not found + */ + public function testExpansionAndUsageOfAnEmptyStringAsValueThrowsException() + { + $c = new Container(<<who_knows_nothing; + } + + /** + * @expectedException Respect\Config\NotFoundException + * @expectedExceptionMessage Item my_name not found + */ + public function testExpansionAndUsageOfANullValueThrowsException() + { + $c = new Container(<<who_knows_nothing; + } + + public function testExpansionInsideASequence() + { + $c = new Container(<<assertEquals( + array(1, 1, 2, 3, 5, 8, 13), + $c->fibonacci, + 'Variable declaration also works inside sequences (or arrays).' + ); + } +} diff --git a/tests/feature/LazyLoading.php b/tests/feature/LazyLoading.php new file mode 100644 index 0000000..627ffc9 --- /dev/null +++ b/tests/feature/LazyLoading.php @@ -0,0 +1,200 @@ +me = 'John Snow'; + + $this->assertEquals( + $c->me, + $c->john, + 'Although the [me] variable didn\'t exist upon container creation, as everything is evaluated through lazy loading.' + ); + } + + /** + * @TODO We should lazy load just what we need. This test should work + * without throwing an Exception. + * @expectedException Respect\Config\NotFoundException + * @expectedExceptionMessage Item me not found + */ + public function testLateExpansionOfJustWhatWeNeed() + { + $c = new Container(<<assertEquals( + 'Jane Doe', + $c->jane, + 'We lazy load just the items we need.' + ); + } + + public function testLazyLoadinessOnMultipleConfigLevels() + { + $c = new Container(<<assertFalse( + $GLOBALS['_SHIT_'], + 'If no instatiation happened, this global variable should be `false`.' + ); + $this->assertInstanceOf( + 'Test\Stub\WheneverIBornIPopulateAGlobalCalled_SHIT_', + $c->foo, + 'Using something to force instatiation.' + ); + $this->assertTrue( + $GLOBALS['_SHIT_'], + 'Believe me now?' + ); + } + + public function testSequencesConstructingLazy() + { + $c = new Container(<<assertInstanceOf( + 'Test\Stub\Bar', + $c->foo->bar + ); + } + + /** + * @group issue + * @ticket 14 + * @ticket 24 + */ + public function testOverwriteOfItemBeforeExpansionOnAnother() + { + $c = new Container(<<my_string = 'Hello World!'; + + $this->assertEquals( + 'Hello World!', + (string) $c->hello + ); + } + + /** + * @group issue + * @ticket 26 + */ + public function testLazyLoadedInstance() + { + $config = " +my_string = 'Hey you!' + +[hello Test\Stub\MyLazyLoadedHelloWorld] +string = [my_string] + +[consumer Test\Stub\MyLazyLoadedHelloWorldConsumer] +hello = [hello] + "; + $expected = 'Hello World!'; + $container = new Container($config); + $container->my_string = $expected; + $this->assertEquals($expected, (string) $container->hello); + $container = new Container($config); + $container->{"hello Test\\Stub\\MyLazyLoadedHelloWorld"} = array('string' => $expected); + $this->assertEquals($expected, (string) $container->hello); + $container = new Container($config); + $container->hello = new \Test\Stub\MyLazyLoadedHelloWorld($expected); + $this->assertEquals($expected, (string) $container->hello); + } +} + +namespace Test\Stub; + +class WheneverIBornIPopulateAGlobalCalled_SHIT_ +{ + function __construct() + { + $GLOBALS['_SHIT_'] = true; + } +} + +class Bar {} + +class Foo +{ + function hey(\DateTime $date) + { + return $date; + } + + function hello($some, Bar $bar) + { + $this->bar = $bar; + } +} + +class MyLazyLoadedHelloWorldConsumer +{ + protected $string; + + public function __construct($hello) + { + $this->string = $hello; + } + + public function __toString() + { + return $this->string; + } +} + +class MyLazyLoadedHelloWorld +{ + protected $string; + + public function __construct($string) + { + $this->string = $string; + } + + public function __toString() + { + return $this->string; + } +} diff --git a/tests/library/Respect/Config/ContainerTest.php b/tests/library/Respect/Config/ContainerTest.php index e657277..c7bd184 100644 --- a/tests/library/Respect/Config/ContainerTest.php +++ b/tests/library/Respect/Config/ContainerTest.php @@ -25,7 +25,8 @@ protected function setUp() )); } - public function tearDown() { + public function tearDown() + { StreamWrapper::releaseOverrides(); } @@ -53,435 +54,45 @@ public function testLoadFile() * @expectedException InvalidArgumentException * @expectedExceptionMessage Invalid input. Must be a valid file or array */ - public function testConfigure() { + public function testLoadStringWithInvalidIni() + { $c = new Container(1); $c->a; } - public function testLoadInvalid() + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Invalid configuration string + */ + public function testLoadFileThatDoesNotExistsThrowException() { - $this->setExpectedException('InvalidArgumentException'); $c = new Container('inexistent.ini'); $c->foo; } - public function testLoadArraySections() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $d = $c->getItem('sec'); - $this->assertEquals('bar', $d['foo']); - $this->assertEquals('bat', $d['baz']); - } - - public function testExpandVars() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $this->assertEquals( - 'mysql:host=localhost;dbname=my_database', $c->getItem('db_dsn') - ); - } - - public function testInstantiator() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $instantiator = $c->getItem('foo', true); - $this->assertEquals('stdClass', $instantiator->getClassName()); - } - - public function testInstantiator2() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $instantiator = $c->getItem('foo', true); - $this->assertEquals('stdClass', $instantiator->getClassName()); - } - - public function testConstants() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $this->assertEquals(E_USER_ERROR, $c->foo); - $this->assertEquals(\PDO::ATTR_ERRMODE, $c->bar); - $this->assertEquals(array(E_USER_ERROR, E_USER_WARNING), $c->faa); - $this->assertEquals(array(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION), $c->bor); - $this->assertEquals(array("foo".DIRECTORY_SEPARATOR."bar", PATH_SEPARATOR), $c->lorem); - $this->assertEquals(array(PATH_SEPARATOR, "foo".DIRECTORY_SEPARATOR."bar"), $c->ipsum); - } - - public function testInstantiatorParams() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $instantiator = $c->getItem('foo', true); - $this->assertEquals('bar', $instantiator->getParam('foo')); - $this->assertEquals('bat', $instantiator->getParam('baz')); - } - public function testInstantiatorMethodCalls() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $dateTime = $c->date; - } - public function testInstantiatorNullMethodCalls() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $conn = $c->conn; - $this->assertNotEmpty($conn->query('SELECT * FROM sqlite_master')->fetch()); - } - - public function testInstantiatorParamsArray() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $instantiator = $c->getItem('foo', true); - $expected = array( - 'abc' => 'bar', - 'def' => 'bat' - ); - $this->assertEquals($expected, $instantiator->getParam('foo')); - } - - public function testInstantiatorParamsBrackets() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $instantiator = $c->getItem('foo', true); - $expectedFoo = array( - 'abc' => array('bat', 'blz'), - 'def' => 'bat' - ); - $expectedBaz = array('bat', 'blz'); - $this->assertEquals($expectedFoo, $instantiator->getParam('foo')); - $this->assertEquals($expectedBaz, $instantiator->getParam('baz')); - } - public function testInstantiatorParamsBracketsReferences() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $instantiator = $c->getItem('foo', true); - $expectedFoo = array( - 'abc' => array('bat', 'blz'), - 'def' => 'bat' - ); - $expectedBaz = array('bat', 'someName'); - $this->assertEquals($expectedFoo, $instantiator->getParam('foo')); - $this->assertEquals($expectedBaz, $instantiator->getParam('baz')); - } - - public function testGetItemLazyLoad() - { - $c = new Container; - $c->foo = function() { return 'ok'; }; - $this->assertEquals('ok', $c->getItem('foo', false)); - } - - public function testClosureWithLoadedFile() - { - $ini = <<panda = function() { return 'ok'; }; - $this->assertEquals('ok', $c->getItem('panda', false)); - } - - public function testLazyLoadinessOnMultipleConfigLevels() - { - $GLOBALS['_SHIT_'] = false; - $ini = <<loadArray(parse_ini_string($ini, true)); - $this->assertFalse($GLOBALS['_SHIT_']); - $GLOBALS['_SHIT_'] = false; - } - - public function testSequencesConstructingLazy() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $this->assertInstanceOf('Respect\Config\Bar', $c->foo->bar); - } - - public function testPascutti() + /** + * @group issue + * @ticket 30 + */ + public function testLateExpansionWhenItemDefinitionIsDonePassingAnArray() { - $GLOBALS['_SHIT_'] = false; - $ini = <<loadArray(parse_ini_string($ini, true)); - $this->assertInstanceOf( - 'Respect\Config\TypeHintWowMuchType', - $c->typed + $this->assertEquals( + 'respect', + $container->account ); } - - public function testLockedContainer() - { - $ini = <<'Hello')); - $this->assertEquals('Hello', $result->bar); - } - public function testLockedContainer2() - { - $ini = <<bar(array('undef'=>'Hello')); - $this->assertEquals('Hello', $result); - } - public function testFactory() - { - $ini = <<now; - $result2 = $c->now; - $this->assertNotSame($result, $result2); - } - public function testDependenciesDoesNotAffectFactories() - { - $ini = <<now; - $result2 = $c->now; - $this->assertSame($result, $result2); - } - public function testByInstanceCallback() - { - $ini = <<assertInstanceOf('DateTime', $result); - $this->assertInstanceOf('DateTime', $result2); - $this->assertTrue($called); - } - public function testByInstanceCallback2() - { - $c = new Container(); - $c(new \DateTime); - $called = false; - $result = $c(function(\DateTime $date) use (&$called) { - $called = true; - return $date; - }); - $result2 = $c['DateTime']; - $this->assertInstanceOf('DateTime', $result); - $this->assertInstanceOf('DateTime', $result2); - $this->assertTrue($called); - } - public function testByMethodCallback() - { - $c = new Container(); - $c(new \DateTime); - $result = $c(array(__NAMESPACE__.'\\Foo', 'hey')); - $this->assertInstanceOf('DateTime', $result); - } - - - public function testClassConstants() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $this->assertEquals(\Respect\Config\TestConstant::CONS_TEST, $c->foo); - } - - public function testClassConstantsAnotherNamespace() - { - class_alias('Respect\Config\TestConstant', 'Respect\Test\Another\Cons'); - $ini = <<loadArray(parse_ini_string($ini, true)); - $this->assertEquals(\Respect\Test\Another\Cons::CONS_TEST, $c->foo); - } - - - public function testInstantiatorWithUnderline() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $instantiator = $c->getItem('foo_bar', true); - $this->assertEquals('stdClass', $instantiator->getClassName()); - } - - public function testClassWithAnotherAndUnderline() - { - $ini = <<loadArray(parse_ini_string($ini, true)); - $this->assertEquals(get_class($c->foo_bar), get_class($c->bar_foo->test)); - } - -} -class Bar {} -class Foo -{ - function hey(\DateTime $date) { - return $date; - } - - function hello($some, Bar $bar) { - $this->bar = $bar; - } } -class WheneverIBornIPopulateAGlobalCalled_SHIT_ -{ - public function __construct(){ - $GLOBALS['_SHIT_'] = true; - } -} - -class DatabaseWow { - public $c; - public function __construct($con) { - $this->c = $con; - } -} - - -class TypeHintWowMuchType { - public $d; - public function __construct(\DateTime $date) { - $this->d = $date; - } -} - -class TestConstant { - const CONS_TEST = "XPTO"; -} - - -class WheneverWithAProperty -{ - public $test; - -} diff --git a/tests/library/Respect/Config/Issues/EnviromentConfigurationTest.php b/tests/library/Respect/Config/Issues/EnviromentConfigurationTest.php deleted file mode 100644 index f990718..0000000 --- a/tests/library/Respect/Config/Issues/EnviromentConfigurationTest.php +++ /dev/null @@ -1,28 +0,0 @@ -assertEquals($expected, $container->account); //respect - } -} - diff --git a/tests/library/Respect/Config/Issues/InstantiatorImplementsInvoke.php b/tests/library/Respect/Config/Issues/InstantiatorImplementsInvoke.php deleted file mode 100644 index 9d8a909..0000000 --- a/tests/library/Respect/Config/Issues/InstantiatorImplementsInvoke.php +++ /dev/null @@ -1,76 +0,0 @@ -my_string = $expected; - $this->assertEquals($expected, (string) $container->hello); - } - - public function testLazyLoadedInstance() - { - $config = " -my_string = 'Hey you!' - -[hello Respect\Config\MyLazyLoadedHelloWorld] -string = [my_string] - -[consumer Respect\Config\MyLazyLoadedHelloWorldConsumer] -hello = [hello] - "; - $expected = 'Hello World!'; - $container = new Container($config); - $container->my_string = $expected; - $this->assertEquals($expected, (string) $container->hello); - $container = new Container($config); - $container->{"hello Respect\\Config\\MyLazyLoadedHelloWorld"} = array('string' => $expected); - $this->assertEquals($expected, (string) $container->hello); - $container = new Container($config); - $container->hello = new MyLazyLoadedHelloWorld($expected); - $this->assertEquals($expected, (string) $container->hello); - } -} -class MyLazyLoadedHelloWorldConsumer -{ - protected $string; - - public function __construct($hello) - { - $this->string = $hello; - } - - public function __toString() - { - return $this->string; - } -} - -class MyLazyLoadedHelloWorld -{ - protected $string; - - public function __construct($string) - { - $this->string = $string; - } - - public function __toString() - { - return $this->string; - } -} - diff --git a/tests/library/Respect/Config/Issues/LazyLoadTest.php b/tests/library/Respect/Config/Issues/LazyLoadTest.php deleted file mode 100644 index 85f21ca..0000000 --- a/tests/library/Respect/Config/Issues/LazyLoadTest.php +++ /dev/null @@ -1,76 +0,0 @@ -my_string = $expected; - $this->assertEquals($expected, (string) $container->hello); - } - - public function testLazyLoadedInstance() - { - $config = " -my_string = 'Hey you!' - -[hello Respect\Config\MyLazyLoadedHelloWorld] -string = [my_string] - -[consumer Respect\Config\MyLazyLoadedHelloWorldConsumer] -hello = [hello] - "; - $expected = 'Hello World!'; - $container = new Container($config); - $container->my_string = $expected; - $this->assertEquals($expected, (string) $container->hello); - $container = new Container($config); - $container->{"hello Respect\\Config\\MyLazyLoadedHelloWorld"} = array('string' => $expected); - $this->assertEquals($expected, (string) $container->hello); - $container = new Container($config); - $container->hello = new MyLazyLoadedHelloWorld($expected); - $this->assertEquals($expected, (string) $container->hello); - } -} -class MyLazyLoadedHelloWorldConsumer -{ - protected $string; - - public function __construct($hello) - { - $this->string = $hello; - } - - public function __toString() - { - return $this->string; - } -} - -class MyLazyLoadedHelloWorld -{ - protected $string; - - public function __construct($string) - { - $this->string = $string; - } - - public function __toString() - { - return $this->string; - } -} - diff --git a/tests/library/Respect/Config/Issues/StaticFactoryTest.php b/tests/library/Respect/Config/Issues/StaticFactoryTest.php deleted file mode 100644 index a306633..0000000 --- a/tests/library/Respect/Config/Issues/StaticFactoryTest.php +++ /dev/null @@ -1,27 +0,0 @@ -setParam('factory', array(array())); - $this->assertAttributeNotEmpty('staticMethodCalls', $i); - $this->assertInstanceOf('DateTime', $i->getInstance()); - } -} - -class StaticTest -{ - private function __construct() {} - public static function factory() - { - return new \DateTime(); - } -} -