diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 3839fbaa0..8a92a2094 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -58,5 +58,8 @@ jobs: - name: Run Check configuration run: php data/bin/check_configuration.php - - name: Run Tests + - name: Run Lime Tests run: php data/bin/symfony symfony:test --trace + + - name: Run PHPUnit Tests + run: php vendor/bin/phpunit diff --git a/.gitignore b/.gitignore index 3c17d4767..0644b20d6 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ lib/plugins/sfDoctrinePlugin/test/functional/fixtures/log/ /vendor /composer.lock .php-cs-fixer.cache +.phpunit.result.cache +phpunit.xml +/tests/fixtures/symfony/log diff --git a/composer.json b/composer.json index 096711cad..61b1397b6 100755 --- a/composer.json +++ b/composer.json @@ -8,7 +8,9 @@ "swiftmailer/swiftmailer": "^5.2 || ^6.0" }, "require-dev": { - "psr/log": "*" + "psr/log": "*", + "phpunit/phpunit": "^9.6", + "symfony/phpunit-bridge": "^7.0" }, "autoload": { "files": ["autoload.php"] @@ -26,5 +28,13 @@ "replace": { "lexpress/symfony1": "^1.5" }, + "scripts": { + "test": [ + "@test:lime", + "@test:phpunit" + ], + "test:lime": "php data/bin/symfony symfony:test --trace", + "test:phpunit": "phpunit" + }, "bin": ["data/bin/symfony"] } diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 000000000..99dc45452 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,27 @@ + + + + + + + + + + + + ./tests/ + + + + + + ./lib/ + + + diff --git a/tests/action/sfComponentTest.php b/tests/action/sfComponentTest.php new file mode 100644 index 000000000..2f151c54c --- /dev/null +++ b/tests/action/sfComponentTest.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require_once __DIR__.'/../fixtures/myComponent.php'; +require_once __DIR__.'/../sfContext.class.php'; +require_once __DIR__.'/../sfNoRouting.class.php'; +require_once __DIR__.'/../sfEventDispatcherTestCase.php'; + +/** + * @internal + */ +class sfComponentTest extends sfEventDispatcherTestCase +{ + private sfContext $context; + + public function setUp(): void + { + $this->context = sfContext::getInstance(array( + 'routing' => 'sfNoRouting', + 'request' => 'sfWebRequest', + )); + + $this->testObject = new myComponent($this->context, 'module', 'action'); + $this->dispatcher = $this->context->getEventDispatcher(); + $this->class = 'component'; + } + + public function testInitialize() + { + $component = new myComponent($this->context, 'module', 'action'); + $this->assertSame($this->context, $component->getContext(), '->initialize() takes a sfContext object as its first argument'); + $component->initialize($this->context, 'module', 'action'); + $this->assertSame($this->context, $component->getContext(), '->initialize() takes a sfContext object as its first argument'); + } + + public function testGetContext() + { + $component = new myComponent($this->context, 'module', 'action'); + + $component->initialize($this->context, 'module', 'action'); + $this->assertSame($this->context, $component->getContext(), '->getContext() returns the current context'); + } + + public function testGetRequest() + { + $component = new myComponent($this->context, 'module', 'action'); + + $component->initialize($this->context, 'module', 'action'); + $this->assertSame($this->context->getRequest(), $component->getRequest(), '->getRequest() returns the current request'); + } + + public function testGetResponse() + { + $component = new myComponent($this->context, 'module', 'action'); + + $component->initialize($this->context, 'module', 'action'); + $this->assertSame($this->context->getResponse(), $component->getResponse(), '->getResponse() returns the current response'); + } + + public function testSetter() + { + $component = new myComponent($this->context, 'module', 'action'); + + $component->foo = array(); + $component->foo[] = 'bar'; + $this->assertSame(array('bar'), $component->foo, '__set() populates component variables'); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 000000000..f362d1471 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bridge\PhpUnit\DeprecationErrorHandler; + +if ('disabled' !== getenv('SYMFONY_DEPRECATIONS_HELPER')) { + DeprecationErrorHandler::register(getenv('SYMFONY_DEPRECATIONS_HELPER')); +} diff --git a/tests/fixtures/myComponent.php b/tests/fixtures/myComponent.php new file mode 100644 index 000000000..008695803 --- /dev/null +++ b/tests/fixtures/myComponent.php @@ -0,0 +1,8 @@ +setReturnValue($arguments[0]); + + return true; + } + } +} diff --git a/tests/sfContext.class.php b/tests/sfContext.class.php new file mode 100644 index 000000000..8fead9ad3 --- /dev/null +++ b/tests/sfContext.class.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +class sfContext +{ + protected static ?sfContext $instance = null; + + public $configuration; + public $request; + public $response; + public $controller; + public $routing; + public $user; + public $storage; + + protected string $sessionPath = ''; + + protected sfEventDispatcher $dispatcher; + + public static function getInstance($factories = array(), $force = false): self + { + if (!isset(self::$instance) || $force) { + self::$instance = new sfContext(); + + self::$instance->sessionPath = sys_get_temp_dir().'/sessions_'.rand(11111, 99999); + self::$instance->storage = new sfSessionTestStorage(array('session_path' => self::$instance->sessionPath)); + + self::$instance->dispatcher = new sfEventDispatcher(); + + foreach ($factories as $type => $class) { + self::$instance->inject($type, $class); + } + } + + return self::$instance; + } + + public function __destruct() + { + sfToolkit::clearDirectory($this->sessionPath); + } + + public static function hasInstance()/*: true*/ + { + return true; + } + + public function getEventDispatcher(): sfEventDispatcher + { + return self::$instance->dispatcher; + } + + public function getModuleName(): string + { + return 'module'; + } + + public function getActionName(): string + { + return 'action'; + } + + public function getConfiguration() + { + return $this->configuration; + } + + public function getRequest() + { + return $this->request; + } + + public function getResponse() + { + return $this->response; + } + + public function getRouting() + { + return $this->routing; + } + + public function getStorage() + { + return $this->storage; + } + + public function getUser() + { + return $this->user; + } + + public function getController() + { + return $this->controller; + } + + public function inject($type, $class, $parameters = array()) + { + switch ($type) { + case 'routing': + $object = new $class($this->dispatcher, null, $parameters); + break; + case 'response': + $object = new $class($this->dispatcher, $parameters); + break; + case 'request': + $object = new $class($this->dispatcher, $this->routing, $parameters); + break; + default: + $object = new $class($this, $parameters); + } + + $this->{$type} = $object; + } +} diff --git a/tests/sfEventDispatcherTestCase.php b/tests/sfEventDispatcherTestCase.php new file mode 100644 index 000000000..3136fc4f9 --- /dev/null +++ b/tests/sfEventDispatcherTestCase.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use PHPUnit\Framework\TestCase; + +require_once __DIR__.'/fixtures/myEventDispatcherTest.php'; + +/** + * @internal + * + * @coversNothing + */ +class sfEventDispatcherTestCase extends TestCase +{ + protected $testObject; + protected $dispatcher; + protected $class; + + public function testExistedMethod() + { + $this->dispatcher->connect($this->class.'.method_not_found', array(myEventDispatcherTest::class, 'newMethod')); + + $this->assertSame('ok', $this->testObject->newMethod('ok'), '__call() accepts new methods via sfEventDispatcher'); + } + + public function testNonExistantMethod() + { + $this->expectException(sfException::class); + + $this->testObject->nonexistantmethodname(); + } +} diff --git a/tests/sfNoRouting.class.php b/tests/sfNoRouting.class.php new file mode 100644 index 000000000..23b8a5942 --- /dev/null +++ b/tests/sfNoRouting.class.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * sfNoRouting class is a very simple routing class that uses GET parameters. + * + * @author Fabien Potencier + * + * @version SVN: $Id: sfNoRouting.class.php 20566 2009-07-29 07:04:01Z fabien $ + */ +class sfNoRouting extends sfRouting +{ + /** + * @see sfRouting + */ + public function getCurrentInternalUri($with_route_name = false) + { + $parameters = $this->mergeArrays($this->defaultParameters, $_GET); + $action = sprintf('%s/%s', $parameters['module'], $parameters['action']); + + // other parameters + unset($parameters['module'], $parameters['action']); + ksort($parameters); + $parameters = count($parameters) ? '?'.http_build_query($parameters, '', '&') : ''; + + return sprintf('%s%s', $action, $parameters); + } + + /** + * @see sfRouting + */ + public function generate($name, $params = array(), $absolute = false) + { + $parameters = $this->mergeArrays($this->defaultParameters, $params); + if ($this->getDefaultParameter('module') == $parameters['module']) { + unset($parameters['module']); + } + if ($this->getDefaultParameter('action') == $parameters['action']) { + unset($parameters['action']); + } + + $parameters = http_build_query($parameters, '', '&'); + + return $this->fixGeneratedUrl('/'.($parameters ? '?'.$parameters : ''), $absolute); + } + + /** + * @see sfRouting + */ + public function parse($url) + { + return array(); + } + + /** + * @see sfRouting + */ + public function getRoutes() + { + return array(); + } + + /** + * @see sfRouting + */ + public function getRoute($name) + { + return null; + } + + /** + * @see sfRouting + */ + public function setRoutes($routes) + { + return array(); + } + + /** + * @see sfRouting + */ + public function hasRoutes() + { + return false; + } + + /** + * @see sfRouting + */ + public function clearRoutes() {} + + protected function mergeArrays($arr1, $arr2) + { + foreach ($arr2 as $key => $value) { + $arr1[$key] = $value; + } + + return $arr1; + } +}